Unverified Commit 96a01de5 authored by Andrey Azov's avatar Andrey Azov Committed by GitHub
Browse files

Add custom landing page for Help (#531)

Includes:
- Updated the Step component to use the label field instead of title, and to accept children
- Added a custom Help landing page (hiding the bottom section in production)
- Hiding the rest of the pages of the Help app in production
- Allow users to see the help landing page in production (links on the Home page and in the launchbar)
- Fixed the styles on the About page
- Hiding the Custom Download button in the launchbar in production
parent d6b45bdd
Pipeline #178919 passed with stages
in 4 minutes and 38 seconds
......@@ -19,6 +19,13 @@ $article-right-padding: 1.5rem;
box-shadow: 0 2px 3px $grey;
}
.spinnerContainer {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
.grid {
height: 100%;
padding-left: 80px;
......
......@@ -27,10 +27,12 @@ import {
TopMenu,
SideMenu
} from 'src/content/app/about/components/about-menu/AboutMenu';
import { CircleLoader } from 'src/shared/components/loader/Loader';
import { Menu as MenuType } from 'src/shared/types/help-and-docs/menu';
import { TextArticleData } from 'src/shared/types/help-and-docs/article';
import helpStyles from '../help/Help.scss';
import styles from './About.scss';
const About = () => {
......@@ -43,15 +45,19 @@ const About = () => {
});
return (
<>
<div>
<AppBar />
<TopMenuBar>
{menu && <TopMenu menu={menu} currentUrl={location.pathname} />}
</TopMenuBar>
</div>
<div className={helpStyles.help}>
<AppBar />
<TopMenuBar>
{menu && <TopMenu menu={menu} currentUrl={location.pathname} />}
</TopMenuBar>
<HelpArticleGrid className={styles.grid}>
{article && <TextArticle article={article} />}
{article ? (
<TextArticle article={article} />
) : (
<div className={styles.spinnerContainer}>
<CircleLoader />
</div>
)}
<aside className={styles.aside}>
{menu && (
<>
......@@ -61,7 +67,7 @@ const About = () => {
)}
</aside>
</HelpArticleGrid>
</>
</div>
);
};
......
......@@ -23,7 +23,6 @@ $leftMargin: 40px;
}
.description {
margin: 25px 40px;
display: flex;
align-items: center;
......@@ -52,12 +51,11 @@ $leftMargin: 40px;
}
.searchDescription {
margin: 14px 36px;
display: flex;
flex-direction: column;
justify-content: flex-start;
gap: 10px;
color: $dark-grey;
.exampleText {
margin: 10px 4px;
}
}
.speciesSelectorButton {
......
......@@ -43,43 +43,46 @@ const BrowserInterstitialInstructions = () => {
<section className={styles.instructionsPanel}>
<div className={styles.instructionsWrapper}>
<div className={styles.stepWrapper}>
<Step count={1} title="Find and add a species" />
<div className={styles.description}>
<ImageButton
className={styles.imageButtonIcon}
status={Status.DISABLED}
image={SpeciesSelectorIcon}
/>
<div className={styles.iconLabel}>Species Selector</div>
</div>
<Step count={1} label="Find and add a species">
<div className={styles.description}>
<ImageButton
className={styles.imageButtonIcon}
status={Status.DISABLED}
image={SpeciesSelectorIcon}
/>
<div className={styles.iconLabel}>Species Selector</div>
</div>
</Step>
</div>
<div className={styles.stepWrapper}>
<Step count={2} title="Return to this app" />
<div className={styles.description}>
<ImageButton
className={styles.imageButtonIcon}
status={Status.DISABLED}
image={BrowserIcon}
/>
<div className={styles.iconLabel}>Genome Browser</div>
</div>
<Step count={2} label="Return to this app">
<div className={styles.description}>
<ImageButton
className={styles.imageButtonIcon}
status={Status.DISABLED}
image={BrowserIcon}
/>
<div className={styles.iconLabel}>Genome Browser</div>
</div>
</Step>
</div>
<div className={styles.stepWrapper}>
<Step
count={3}
title="Use Search or the example links to view a gene or region"
/>
<div className={styles.searchDescription}>
<ImageButton
className={styles.searchButtonIcon}
status={Status.DISABLED}
image={SearchIcon}
/>
<div className={styles.exampleText}>Example gene</div>
<div className={styles.exampleText}>Example region</div>
</div>
label="Use Search or the example links to view a gene or region"
>
<div className={styles.searchDescription}>
<ImageButton
className={styles.searchButtonIcon}
status={Status.DISABLED}
image={SearchIcon}
/>
<div className={styles.exampleText}>Example gene</div>
<div className={styles.exampleText}>Example region</div>
</div>
</Step>
</div>
<PrimaryButton
......
......@@ -23,7 +23,6 @@ $leftMargin: 40px;
}
.description {
margin: 25px 40px;
display: flex;
align-items: center;
......@@ -52,12 +51,10 @@ $leftMargin: 40px;
}
.searchDescription {
margin: 14px 36px;
display: flex;
flex-direction: column;
gap: 10px;
color: $dark-grey;
.exampleText {
margin: 10px 4px;
}
}
.speciesSelectorButton {
......
......@@ -43,42 +43,45 @@ const EntityViewerInterstitialInstructions = () => {
<section className={styles.instructionsPanel}>
<div className={styles.instructionsWrapper}>
<div className={styles.stepWrapper}>
<Step count={1} title="Find and add a species" />
<div className={styles.description}>
<ImageButton
className={styles.imageButtonIcon}
status={Status.DISABLED}
image={SpeciesSelectorIcon}
/>
<div className={styles.iconLabel}>Species Selector</div>
</div>
<Step count={1} label="Find and add a species">
<div className={styles.description}>
<ImageButton
className={styles.imageButtonIcon}
status={Status.DISABLED}
image={SpeciesSelectorIcon}
/>
<div className={styles.iconLabel}>Species Selector</div>
</div>
</Step>
</div>
<div className={styles.stepWrapper}>
<Step count={2} title="Return to this app" />
<div className={styles.description}>
<ImageButton
className={styles.imageButtonIcon}
status={Status.DISABLED}
image={EntityViewerIcon}
/>
<div className={styles.iconLabel}>Entity Viewer</div>
</div>
<Step count={2} label="Return to this app">
<div className={styles.description}>
<ImageButton
className={styles.imageButtonIcon}
status={Status.DISABLED}
image={EntityViewerIcon}
/>
<div className={styles.iconLabel}>Entity Viewer</div>
</div>
</Step>
</div>
<div className={styles.stepWrapper}>
<Step
count={3}
title="Use Search or the example links to view a gene"
/>
<div className={styles.searchDescription}>
<ImageButton
className={styles.searchButtonIcon}
status={Status.DISABLED}
image={SearchIcon}
/>
<div className={styles.exampleText}>Example gene</div>
</div>
label="Use Search or the example links to view a gene"
>
<div className={styles.searchDescription}>
<ImageButton
className={styles.searchButtonIcon}
status={Status.DISABLED}
image={SearchIcon}
/>
<div className={styles.exampleText}>Example gene</div>
</div>
</Step>
</div>
<PrimaryButton
......
......@@ -19,9 +19,9 @@ import { useLocation } from 'react-router';
import useApiService from 'src/shared/hooks/useApiService';
import HelpMenu from './help-menu/HelpMenu';
import HelpMenu from './components/help-menu/HelpMenu';
import HelpLanding from './components/help-landing/HelpLanding';
import {
IndexArticle,
TextArticle,
RelatedArticles,
HelpArticleGrid,
......@@ -31,28 +31,36 @@ import {
import { Menu as MenuType } from 'src/shared/types/help-and-docs/menu';
import {
TextArticleData,
VideoArticleData,
IndexArticleData
VideoArticleData
} from 'src/shared/types/help-and-docs/article';
import styles from './Help.scss';
type ArticleData = TextArticleData | VideoArticleData | IndexArticleData;
type ArticleData = TextArticleData | VideoArticleData;
const Help = () => {
const location = useLocation();
const isIndexPage = isIndexRoute(location.pathname);
const { data: menu } = useApiService<MenuType>({
endpoint: `/api/docs/menus?name=help`
});
const { data: article } = useApiService<any>({
endpoint: `/api/docs/article?url=${encodeURIComponent(location.pathname)}`
endpoint: `/api/docs/article?url=${encodeURIComponent(location.pathname)}`,
skip: isIndexPage
});
const main = isIndexPage ? (
<main className={styles.main}>
<HelpLanding />
</main>
) : article ? (
<MainContent article={article} />
) : null;
return (
<div className={styles.help}>
<AppBar />
{menu && <HelpMenu menu={menu} currentUrl={location.pathname} />}
{article && <MainContent article={article} />}
{main}
</div>
);
};
......@@ -63,26 +71,31 @@ const AppBar = () => {
const MainContent = (props: { article: ArticleData }) => {
const { article } = props;
let content;
if (article.type === 'index') {
content = <IndexArticle article={article} />;
} else if (['article', 'video'].includes(article.type)) {
const renderedArticle =
article.type === 'article' ? (
<TextArticle article={article} />
) : (
<VideoArticle video={article} />
);
content = (
<HelpArticleGrid className={styles.articleGrid}>
{renderedArticle}
{!!article.related_articles.length && (
<RelatedArticles articles={article.related_articles} />
)}
</HelpArticleGrid>
);
if (article.type !== 'article' && article.type !== 'video') {
return null;
}
const renderedArticle =
article.type === 'article' ? (
<TextArticle article={article} />
) : (
<VideoArticle video={article} />
);
const content = (
<HelpArticleGrid className={styles.articleGrid}>
{renderedArticle}
{!!article.related_articles.length && (
<RelatedArticles articles={article.related_articles} />
)}
</HelpArticleGrid>
);
return <main className={styles.main}>{content}</main>;
};
const isIndexRoute = (pathname: string) => {
// handle both /help and /help/
return pathname.replaceAll('/', '') === 'help';
};
export default Help;
@import 'src/styles/common';
$indent-left: 34px;
.helpLanding {
height: 100%;
overflow: auto;
padding: 0 40px 40px 0;
.showHideWrapper {
display: inline-block;
margin-left: 3ch;
}
section + section {
margin-top: 40px;
}
}
.appsGrid {
display: grid;
grid-template-columns: repeat(auto-fit, 324px);
column-gap: 96px;
row-gap: 30px;
margin-left: $indent-left;
}
.stepsGrid {
display: grid;
grid-template-columns: repeat(auto-fit, 218px);
column-gap: 200px;
max-width: 1200px;
background-color: $light-grey;
margin-top: 1ch;
padding: 20px $indent-left 40px $indent-left;
}
.appBlock {
ul {
margin-top: 1ch;
list-style-type: disc;
padding-left: 15px;
font-size: 12px;
color: $dark-grey;
}
}
.appLabel {
line-height: 1;
margin-bottom: 16px;
}
.searchIcon {
width: 30px;
height: 30px;
fill: $dark-grey;
}
.stepChildren {
display: flex;
flex-direction: column;
gap: 14px;
line-height: 1;
}
.lastSectionGrid {
display: grid;
grid-template-columns: repeat(2, 324px);
column-gap: calc(96px + #{$indent-left});
row-gap: 30px;
p {
padding-left: $indent-left;
}
}
/**
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useState } from 'react';
import LabelledAppIcon from '../labelled-app-icon/LabelledAppIcon';
import { Step } from 'src/shared/components/step/Step';
import ShowHide from 'src/shared/components/show-hide/ShowHide';
import { isEnvironment, Environment } from 'src/shared/helpers/environment';
import { ReactComponent as SearchIcon } from 'static/img/sidebar/search.svg';
import styles from './HelpLanding.scss';
const HelpLanding = () => {
return (
<div className={styles.helpLanding}>
<AppsSection />
<StartUsingSection />
{
/* We don't yet have full documentation announced in this section */
isEnvironment([Environment.DEVELOPMENT, Environment.INTERNAL]) && (
<LastSection />
)
}
</div>
);
};
const AppsSection = () => {
return (
<section>
<h1>Ensembl apps</h1>
<div className={styles.appsGrid}>
<div className={styles.appBlock}>
<div className={styles.appLabel}>
<LabelledAppIcon app="speciesSelector" />
</div>
Create &amp; manage your own species list
<ul>
<li>opened from the launchbar at the top of every page</li>
<li>add as many species as you want</li>
<li>your species will then be available across all apps</li>
<li>use the species tabs to see information about each species</li>
</ul>
</div>
<div className={styles.appBlock}>
<div className={styles.appLabel}>
<LabelledAppIcon app="genomeBrowser" />
</div>
Look at genes &amp; transcripts in their genetic context
<ul>
<li>opened from the launchbar at the top of every page</li>
<li>
view an example gene or region, or search for your own genes
</li>
<li>download the data you want</li>
</ul>
</div>
<div className={styles.appBlock}>
<div className={styles.appLabel}>
<LabelledAppIcon app="entityViewer" />
</div>
Get gene &amp; transcript information
<ul>
<li>opened from the launchbar at the top of every page</li>
<li>see all the information we have for your gene</li>
<li>download the data you want</li>
</ul>
</div>
</div>
</section>
);
};
const StartUsingSection = () => {
const [isExpanded, setIsExpanded] = useState(false);
const toggleExpanded = () => setIsExpanded(!isExpanded);
return (
<section>
<h1>Start using the apps</h1>
<div>
<span>A step-by-step guide to using an app for the first time</span>
<ShowHide
label="Show me what to do"
isExpanded={isExpanded}
onClick={toggleExpanded}
classNames={{ wrapper: styles.showHideWrapper }}
/>
</div>
{isExpanded && (
<div className={styles.stepsGrid}>
<div>
<Step count={1} label="Find and add a species">
<LabelledAppIcon app="speciesSelector" size="small" />
</Step>