Skip to content

Commit

Permalink
Add page specific head title (#3526)
Browse files Browse the repository at this point in the history
* Add @types/react-helmet

* Add page specific head title

* Add head titles for onboarding and preference pages

* Align react-helmet version to 6.1.0

* Refactor head title to support chained titles

* Move head title out of step menu definition

* Unify imports
  • Loading branch information
tuliren committed May 25, 2021
1 parent 68504d8 commit d24b5c5
Show file tree
Hide file tree
Showing 23 changed files with 177 additions and 39 deletions.
37 changes: 25 additions & 12 deletions airbyte-webapp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion airbyte-webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"react-copy-to-clipboard": "^5.0.2",
"react-dom": "17.0.1",
"react-dropzone": "^11.2.4",
"react-helmet": "^5.2.1",
"react-helmet": "6.1.0",
"react-intl": "^3.12.0",
"react-pose": "^4.0.10",
"react-router-dom": "^5.1.2",
Expand All @@ -57,6 +57,7 @@
"@types/query-string": "^6.3.0",
"@types/react": "17.0.2",
"@types/react-dom": "17.0.1",
"@types/react-helmet": "6.1.0",
"@types/react-router-dom": "^5.1.3",
"@types/react-table": "^7.0.12",
"@types/react-widgets": "^4.4.2",
Expand Down
48 changes: 48 additions & 0 deletions airbyte-webapp/src/components/HeadTitle/HeadTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from "react";
import { Helmet } from "react-helmet";
import { useIntl } from "react-intl";

const AIRBYTE = "Airbyte";
const SEPARATOR = "|";

type FormattedHeadTitle = {
id: string;
values?: Record<string, string>;
};

type StringHeadTitle = {
title: string;
};

type HeadTitleDefinition = FormattedHeadTitle | StringHeadTitle;

const isStringTitle = (v: HeadTitleDefinition): v is StringHeadTitle => {
return "title" in v;
};

type IProps = {
titles: HeadTitleDefinition[];
};

/**
* Titles defined by {@link HeadTitleDefinition} will be
* chained together with the {@link SEPARATOR}.
*/
const HeadTitle: React.FC<IProps> = ({ titles }) => {
const intl = useIntl();

const getTitle = (d: HeadTitleDefinition): string => {
return isStringTitle(d)
? d.title
: intl.formatMessage({ id: d.id }, d.values);
};

const headTitle = titles.map(getTitle).join(` ${SEPARATOR} `);
return (
<Helmet titleTemplate={`${AIRBYTE} ${SEPARATOR} %s`} defaultTitle={AIRBYTE}>
<title>{headTitle}</title>
</Helmet>
);
};

export default HeadTitle;
3 changes: 3 additions & 0 deletions airbyte-webapp/src/components/HeadTitle/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import HeadTitle from "./HeadTitle";

export default HeadTitle;
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,25 @@ const Page = styled.div`
height: 100%;
`;

/**
* @param headTitle the title shown in the browser toolbar
* @param pageTitle the title shown on the page
*/
type IProps = {
title?: React.ReactNode;
headTitle?: React.ReactNode;
pageTitle?: React.ReactNode;
children?: React.ReactNode;
};

const MainPageWithScroll: React.FC<IProps> = ({ title, children }) => {
const MainPageWithScroll: React.FC<IProps> = ({
headTitle,
pageTitle,
children,
}) => {
return (
<Page>
{title}
{headTitle}
{pageTitle}
<Content>{children}</Content>
</Page>
);
Expand Down
14 changes: 8 additions & 6 deletions airbyte-webapp/src/components/StepsMenu/StepsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import styled from "styled-components";

import Step from "./components/Step";

export type StepMenuItem = {
id: string;
name: string | React.ReactNode;
status?: string;
onSelect?: () => void;
};

type IProps = {
lightMode?: boolean;
data: Array<{
id: string;
name: string | React.ReactNode;
status?: string;
onSelect?: () => void;
}>;
data: StepMenuItem[];
activeStep?: string;
onSelect?: (id: string) => void;
};
Expand Down
2 changes: 2 additions & 0 deletions airbyte-webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"form.pkSelected": "{count, plural, =0 { } one {{items}} other {# keys selected}}",
"form.url.error": "field must be a valid URL",

"preferences.headTitle": "Preferences",
"preferences.title": "Specify your preferences",
"preferences.anonymizeUsage": "Anonymize usage data collection",
"preferences.collectData": "We collect data only for product improvements, see the <docs>docs</docs>.",
Expand All @@ -102,6 +103,7 @@
"preferences.unsubscribeAnyTime": " You can unsubscribe any time.",
"preferences.securityUpdates": "Receive emails about security updates.",

"onboarding.headTitle": "Get Started!",
"onboarding.title": "Let’s set up your first connection.",
"onboarding.subtitle": "Connections are automated data pipelines that sync data from a source to a destination.",
"onboarding.createSource": "Create a source",
Expand Down
7 changes: 5 additions & 2 deletions airbyte-webapp/src/pages/AdminPage/AdminPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import styled from "styled-components";
import MainPageWithScroll from "components/MainPageWithScroll";
import PageTitle from "components/PageTitle";
import StepsMenu from "components/StepsMenu";
import { StepMenuItem } from "components/StepsMenu/StepsMenu";
import LoadingPage from "components/LoadingPage";
import SourcesView from "./components/SourcesView";
import DestinationsView from "./components/DestinationsView";
import CreateConnector from "./components/CreateConnector";
import ConfigurationView from "./components/ConfigurationView";
import HeadTitle from "components/HeadTitle";

const Content = styled.div`
padding-top: 4px;
Expand All @@ -24,7 +26,7 @@ enum StepsTypes {
}

const AdminPage: React.FC = () => {
const steps = [
const steps: StepMenuItem[] = [
{
id: StepsTypes.SOURCES,
name: <FormattedMessage id="admin.sources" />,
Expand Down Expand Up @@ -54,7 +56,8 @@ const AdminPage: React.FC = () => {

return (
<MainPageWithScroll
title={
headTitle={<HeadTitle titles={[{ id: "sidebar.admin" }]} />}
pageTitle={
<PageTitle
withLine
title={<FormattedMessage id="sidebar.admin" />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ImportConfigurationModal from "./ImportConfigurationModal";
import DeploymentService from "core/resources/DeploymentService";
import LogsContent from "./LogsContent";
import LoadingButton from "components/Button/LoadingButton";
import HeadTitle from "components/HeadTitle";

const Content = styled.div`
max-width: 813px;
Expand Down Expand Up @@ -84,6 +85,9 @@ const ConfigurationView: React.FC = () => {

return (
<Content>
<HeadTitle
titles={[{ id: "sidebar.admin" }, { id: "admin.configuration" }]}
/>
<ControlContent title={<FormattedMessage id="admin.export" />}>
<ButtonContent>
<LoadingButton onClick={onExport} isLoading={loadingExport}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DestinationResource } from "core/resources/Destination";
import { DestinationDefinition } from "core/resources/DestinationDefinition";
import UpgradeAllButton from "./UpgradeAllButton";
import useNotification from "components/hooks/services/useNotification";
import HeadTitle from "components/HeadTitle";

const DestinationsView: React.FC = () => {
const [successUpdate, setSuccessUpdate] = useState(false);
Expand Down Expand Up @@ -160,6 +161,9 @@ const DestinationsView: React.FC = () => {

return (
<>
<HeadTitle
titles={[{ id: "sidebar.admin" }, { id: "admin.destinations" }]}
/>
{usedDestinationDefinitions.length ? (
<Block>
<Title bold>
Expand Down
2 changes: 2 additions & 0 deletions airbyte-webapp/src/pages/AdminPage/components/SourcesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import SourceDefinitionResource, {
import { SourceResource } from "core/resources/Source";
import UpgradeAllButton from "./UpgradeAllButton";
import useNotification from "components/hooks/services/useNotification";
import HeadTitle from "components/HeadTitle";

const SourcesView: React.FC = () => {
const [successUpdate, setSuccessUpdate] = useState(false);
Expand Down Expand Up @@ -156,6 +157,7 @@ const SourcesView: React.FC = () => {

return (
<>
<HeadTitle titles={[{ id: "sidebar.admin" }, { id: "admin.sources" }]} />
{usedSourcesDefinitions.length ? (
<Block>
<Title bold>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ConnectionsTable from "./components/ConnectionsTable";
import { Routes } from "pages/routes";
import useRouter from "components/hooks/useRouterHook";
import EmptyResource from "components/EmptyResourceBlock";
import HeadTitle from "components/HeadTitle";

const Content = styled(ContentCard)`
margin: 0 32px 0 27px;
Expand All @@ -32,7 +33,8 @@ const AllConnectionsPage: React.FC = () => {

return (
<MainPageWithScroll
title={
headTitle={<HeadTitle titles={[{ id: "sidebar.connections" }]} />}
pageTitle={
<PageTitle
title={<FormattedMessage id="sidebar.connections" />}
endComponent={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FormattedMessage } from "react-intl";
import { useResource } from "rest-hooks";

import PageTitle from "components/PageTitle";
import HeadTitle from "components/HeadTitle";
import useRouter from "components/hooks/useRouterHook";
import StepsMenu from "components/StepsMenu";
import StatusView from "./components/StatusView";
Expand Down Expand Up @@ -129,7 +130,21 @@ const ConnectionItemPage: React.FC<ConnectionItemPageProps> = ({

return (
<MainPageWithScroll
title={
headTitle={
<HeadTitle
titles={[
{ id: "sidebar.connections" },
{
id: "connection.fromTo",
values: {
source: connection.source?.name,
destination: connection.destination?.name,
},
},
]}
/>
}
pageTitle={
<PageTitle
withLine
title={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import SourceResource from "core/resources/Source";
import DestinationResource from "core/resources/Destination";
import DestinationDefinitionResource from "core/resources/DestinationDefinition";
import SourceDefinitionResource from "core/resources/SourceDefinition";
import HeadTitle from "components/HeadTitle";

type IProps = {
type: "source" | "destination" | "connection";
Expand Down Expand Up @@ -180,20 +181,24 @@ const CreationFormPage: React.FC<IProps> = ({ type }) => {
},
];

const titleId = () => {
switch (type) {
case "connection":
return "connection.newConnectionTitle";
case "destination":
return "destinations.newDestinationTitle";
case "source":
return "sources.newSourceTitle";
}
};

return (
<MainPageWithScroll
title={
headTitle={<HeadTitle titles={[{ id: titleId() }]} />}
pageTitle={
<PageTitle
withLine
title={
type === "connection" ? (
<FormattedMessage id="connection.newConnectionTitle" />
) : type === "destination" ? (
<FormattedMessage id="destinations.newDestinationTitle" />
) : (
<FormattedMessage id="sources.newSourceTitle" />
)
}
title={<FormattedMessage id={titleId()} />}
middleComponent={
<StepsMenu lightMode data={steps} activeStep={currentStep} />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import config from "config";
import ContentCard from "components/ContentCard";
import EmptyResource from "components/EmptyResourceBlock";
import DestinationResource from "core/resources/Destination";
import HeadTitle from "components/HeadTitle";

const Content = styled(ContentCard)`
margin: 0 32px 0 27px;
Expand All @@ -29,6 +30,7 @@ const AllDestinationsPage: React.FC = () => {

return (
<>
<HeadTitle titles={[{ id: "admin.destinations" }]} />
<PageTitle
title={<FormattedMessage id="admin.destinations" />}
endComponent={
Expand Down
Loading

0 comments on commit d24b5c5

Please sign in to comment.