From 727e7643aa458a215b41ef46557dfa721372a028 Mon Sep 17 00:00:00 2001 From: czgu Date: Tue, 19 Jan 2021 14:05:26 -0500 Subject: [PATCH] Add the ability to customize app name (#379) https://github.com/pinterest/querybook/pull/379 --- .storybook/webpack.js | 10 ++++++ docs/admin_guide/custom_app_name.md | 19 ++++++++++ jest.config.js | 4 +++ .../__snapshots__/QuerybookLogo.test.js.snap | 35 +++---------------- .../AdminAppSidebar/AdminAppEntitySidebar.tsx | 4 +-- querybook/webapp/components/App/App.tsx | 27 -------------- .../webapp/components/AppAdmin/AppAdmin.tsx | 5 +-- .../DataTableViewOverview.tsx | 3 +- .../DataTableViewQueryUsers.tsx | 3 +- .../DataTableViewSourceQuery.tsx | 3 +- .../EnvironmentAppRouter/InfoMenuRoute.tsx | 2 +- .../UserSettingsMenuRoute.tsx | 2 +- .../EnvironmentsRouter/EnvironmentsRouter.tsx | 7 ++-- querybook/webapp/components/Info/Tours.tsx | 7 ++-- .../InfoMenuButton/QuerybookVersion.tsx | 19 ++-------- .../QueryEngineStatusViewer.tsx | 4 ++- .../components/QueryExecution/QueryError.tsx | 9 ++--- querybook/webapp/components/SetUp/SetUp.tsx | 3 +- .../UIGuide/QuerybookSidebarUIGuide.tsx | 12 ++++--- .../components/UnauthPage/UnauthPage.tsx | 10 ++++-- querybook/webapp/const/chartColors.ts | 4 +-- querybook/webapp/hooks/useForwardedRef.ts | 2 +- querybook/webapp/lib/querybookUI.ts | 9 ++--- querybook/webapp/lib/utils/global.ts | 19 ++++++++++ querybook/webapp/stylesheets/_variables.scss | 5 --- querybook/webapp/types.d.ts | 32 +++++++++++++++++ querybook/webapp/ui/Menu/ListMenu.tsx | 2 +- .../webapp/ui/QuerybookLogo/QuerybookLogo.tsx | 33 +++++++++-------- webpack.config.js | 8 ++++- 29 files changed, 169 insertions(+), 133 deletions(-) create mode 100644 docs/admin_guide/custom_app_name.md create mode 100644 querybook/webapp/lib/utils/global.ts create mode 100644 querybook/webapp/types.d.ts diff --git a/.storybook/webpack.js b/.storybook/webpack.js index 304b6aa2c..5351d8ecf 100644 --- a/.storybook/webpack.js +++ b/.storybook/webpack.js @@ -1,5 +1,6 @@ const postcssPresetEnv = require('postcss-preset-env'); const path = require('path'); +const webpack = require('webpack'); module.exports = async (config) => { config.module.rules.push({ @@ -39,5 +40,14 @@ module.exports = async (config) => { path.resolve('./querybook/webapp'), ]; + config.plugins.push( + new webpack.DefinePlugin({ + __VERSION__: JSON.stringify(require('../package.json').version), + __APPNAME__: JSON.stringify( + process.env.QUERYBOOK_APPNAME ?? 'Querybook' + ), + }) + ); + return config; }; diff --git a/docs/admin_guide/custom_app_name.md b/docs/admin_guide/custom_app_name.md new file mode 100644 index 000000000..b08a21d9d --- /dev/null +++ b/docs/admin_guide/custom_app_name.md @@ -0,0 +1,19 @@ +--- +id: custom_app_name +title: Custom App Name +sidebar_label: Custom App Name +--- + +By default, the frontend website displays the application name as `Querybook`. However, you may want to use another name that aligns with your backend systems. This name change is for frontend display only and does not impact the backend configurations. + +To do so, rebuild the Webpack files with the environment variable `QUERYBOOK_APPNAME`. For example, you can have a Dockerfile as such: + +```Dockerfile +FROM querybook:latest + +RUN QUERYBOOK_APPNAME=Example ./node_modules/.bin/webpack --env.NODE_ENV=production +``` + +The website served by this docker image will show "Welcome to Example" on the homepage with the browser title as "Example". + +You can use any utf8 characters but it’s recommended to keep the length to be roughly shorter than 15 English characters to prevent text-overflow. diff --git a/jest.config.js b/jest.config.js index c524a5679..65a4e01a7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,6 +16,10 @@ const config = { rootDir: './querybook/webapp/', modulePaths: [''], globalSetup: '/__tests__/setup/jest-global-setup.js', + globals: { + __VERSION__: '1.0.0', + __APPNAME__: 'Querybook', + }, }; module.exports = config; diff --git a/querybook/webapp/__tests__/ui/__snapshots__/QuerybookLogo.test.js.snap b/querybook/webapp/__tests__/ui/__snapshots__/QuerybookLogo.test.js.snap index 3dc43929c..c79bc2ee3 100644 --- a/querybook/webapp/__tests__/ui/__snapshots__/QuerybookLogo.test.js.snap +++ b/querybook/webapp/__tests__/ui/__snapshots__/QuerybookLogo.test.js.snap @@ -7,16 +7,7 @@ exports[`matches enzyme snapshots matches snapshot - size 1`] = ` - - Query - - - book - + Querybook `; @@ -28,38 +19,20 @@ exports[`matches enzyme snapshots matches snapshot 1`] = ` - - Query - - - book - + Querybook `; exports[`matches test renderer snapshot serializes the styles 1`] = ` - - Query - - - book - + Querybook `; diff --git a/querybook/webapp/components/AdminAppSidebar/AdminAppEntitySidebar.tsx b/querybook/webapp/components/AdminAppSidebar/AdminAppEntitySidebar.tsx index a85123037..aa363687b 100644 --- a/querybook/webapp/components/AdminAppSidebar/AdminAppEntitySidebar.tsx +++ b/querybook/webapp/components/AdminAppSidebar/AdminAppEntitySidebar.tsx @@ -9,6 +9,7 @@ import { IconButton } from 'ui/Button/IconButton'; import './AdminAppEntitySidebar.scss'; import { QuerybookLogo } from 'ui/QuerybookLogo/QuerybookLogo'; +import { getAppName } from 'lib/utils/global'; interface IAdminAppEntitySidebarProps { selectedEntity: AdminEntity; @@ -70,12 +71,11 @@ export const AdminAppEntitySidebar: React.FunctionComponent - Back to Querybook + Back to {getAppName()} diff --git a/querybook/webapp/components/App/App.tsx b/querybook/webapp/components/App/App.tsx index 8dd93630c..6c190687b 100644 --- a/querybook/webapp/components/App/App.tsx +++ b/querybook/webapp/components/App/App.tsx @@ -5,39 +5,12 @@ import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { Provider } from 'react-redux'; -import { - IColumnStatsAnalyzer, - IColumnDetector, - IColumnTransformer, -} from 'lib/query-result/types'; import { AppRouter } from 'components/AppRouter/AppRouter'; import { ConfirmationManager } from 'components/ConfirmationManager/ConfirmationManager'; import { ToastManager } from 'ui/ToastManager/ToastManager'; import { reduxStore } from 'redux/store'; -// TODO: decouple this with App.tsx -declare global { - /* eslint-disable @typescript-eslint/naming-convention */ - interface Window { - reduxStore?: typeof reduxStore; - receiveChildMessage?: () => void; - - // Web Plugin Variables - NO_ENVIRONMENT_MESSAGE?: string; - CUSTOM_LANDING_PAGE?: { - // Two modes of custom landing page - // replace: replace the entire landing page with custom content - // not specified: add the custom content to the middle of the - // landing page - mode?: 'replace'; - renderer: () => React.ReactElement; - }; - CUSTOM_COLUMN_STATS_ANALYZERS?: IColumnStatsAnalyzer[]; - CUSTOM_COLUMN_DETECTORS?: IColumnDetector[]; - CUSTOM_COLUMN_TRANSFORMERS?: IColumnTransformer[]; - } -} // Make debugging easier window.reduxStore = reduxStore; diff --git a/querybook/webapp/components/AppAdmin/AppAdmin.tsx b/querybook/webapp/components/AppAdmin/AppAdmin.tsx index 4707851c5..f0ecb5225 100644 --- a/querybook/webapp/components/AppAdmin/AppAdmin.tsx +++ b/querybook/webapp/components/AppAdmin/AppAdmin.tsx @@ -3,8 +3,8 @@ import { useSelector } from 'react-redux'; import { Route, Switch, useParams } from 'react-router-dom'; import history from 'lib/router-history'; +import { getAppName } from 'lib/utils/global'; import { IStoreState } from 'redux/store/types'; -import { AdminEntity } from './types'; import { useDataFetch } from 'hooks/useDataFetch'; import { AdminAppEntitySidebar } from 'components/AdminAppSidebar/AdminAppEntitySidebar'; @@ -25,6 +25,7 @@ import { FourOhThree } from 'ui/ErrorPage/FourOhThree'; import { Icon } from 'ui/Icon/Icon'; import { Sidebar } from 'ui/Sidebar/Sidebar'; +import { AdminEntity } from './types'; import './AppAdmin.scss'; const ENTITY_SIDEBAR_WIDTH = 200; @@ -122,7 +123,7 @@ export const AppAdmin: React.FunctionComponent = () => {
- Welcome to the Querybook Admin App + Welcome to the {getAppName()} Admin App
All your settings are here. diff --git a/querybook/webapp/components/DataTableViewOverview/DataTableViewOverview.tsx b/querybook/webapp/components/DataTableViewOverview/DataTableViewOverview.tsx index b8916e7bf..4c49846a9 100644 --- a/querybook/webapp/components/DataTableViewOverview/DataTableViewOverview.tsx +++ b/querybook/webapp/components/DataTableViewOverview/DataTableViewOverview.tsx @@ -10,6 +10,7 @@ import { } from 'const/metastore'; import { navigateWithinEnv } from 'lib/utils/query-string'; import { generateFormattedDate } from 'lib/utils/datetime'; +import { getAppName } from 'lib/utils/global'; import { titleize } from 'lib/utils'; import { getHumanReadableByteSize } from 'lib/utils/number'; @@ -135,7 +136,7 @@ export class DataTableViewOverview extends React.PureComponent<

- First created in Querybook at{' '} + First created in {getAppName()} at{' '} {generateFormattedDate(table.created_at)}.

diff --git a/querybook/webapp/components/DataTableViewQueryExample/DataTableViewQueryUsers.tsx b/querybook/webapp/components/DataTableViewQueryExample/DataTableViewQueryUsers.tsx index 9dc7868cf..034b5a135 100644 --- a/querybook/webapp/components/DataTableViewQueryExample/DataTableViewQueryUsers.tsx +++ b/querybook/webapp/components/DataTableViewQueryExample/DataTableViewQueryUsers.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; +import { getAppName } from 'lib/utils/global'; import { IStoreState, Dispatch } from 'redux/store/types'; import { fetchTopQueryUsersIfNeeded } from 'redux/dataSources/action'; @@ -41,7 +42,7 @@ export const DataTableViewQueryUsers: React.FC<{ const viewersDOM = loading ? ( ) : !topQueryUsers?.length ? ( -

No user has queried this table on Querybook.
+
No user has queried this table on {getAppName()}.
) : ( ({ diff --git a/querybook/webapp/components/DataTableViewSourceQuery/DataTableViewSourceQuery.tsx b/querybook/webapp/components/DataTableViewSourceQuery/DataTableViewSourceQuery.tsx index 5bc1724d0..cbc635bf7 100644 --- a/querybook/webapp/components/DataTableViewSourceQuery/DataTableViewSourceQuery.tsx +++ b/querybook/webapp/components/DataTableViewSourceQuery/DataTableViewSourceQuery.tsx @@ -6,6 +6,7 @@ import { ILineageCollection, } from 'const/metastore'; import { getWithinEnvUrl } from 'lib/utils/query-string'; +import { getAppName } from 'lib/utils/global'; import { Loading } from 'ui/Loading/Loading'; import { Table } from 'ui/Table/Table'; import { CodeHighlight } from 'ui/CodeHighlight/CodeHighlight'; @@ -126,7 +127,7 @@ const DataJobMetadataInfo: React.FC<{ ) { const queryExecutionId = dataJobMetadata.job_info['query_execution_id']; queryExecutionUrlRows.push({ - name: `Created in Querybook`, + name: `Created in ${getAppName()}`, value: ( = ({ infoType === 'shortcut' ? 'Keyboard Shortcuts' : infoType === 'tour' - ? 'Querybook Tutorials' + ? 'Tutorials' : 'Frequently Asked Questions'; return isModal ? ( diff --git a/querybook/webapp/components/EnvironmentAppRouter/UserSettingsMenuRoute.tsx b/querybook/webapp/components/EnvironmentAppRouter/UserSettingsMenuRoute.tsx index dbaa40659..548ebcd39 100644 --- a/querybook/webapp/components/EnvironmentAppRouter/UserSettingsMenuRoute.tsx +++ b/querybook/webapp/components/EnvironmentAppRouter/UserSettingsMenuRoute.tsx @@ -20,7 +20,7 @@ export const UserSettingsMenuRoute: React.FunctionComponent onHide={ isModalRoute ? history.goBack : () => navigateWithinEnv('/') } - title="Querybook User Settings" + title="User Settings" className="with-padding" > {contentDOM} diff --git a/querybook/webapp/components/EnvironmentsRouter/EnvironmentsRouter.tsx b/querybook/webapp/components/EnvironmentsRouter/EnvironmentsRouter.tsx index 8802c587b..e69405237 100644 --- a/querybook/webapp/components/EnvironmentsRouter/EnvironmentsRouter.tsx +++ b/querybook/webapp/components/EnvironmentsRouter/EnvironmentsRouter.tsx @@ -20,13 +20,14 @@ import { } from 'redux/environment/selector'; import { IStoreState, Dispatch } from 'redux/store/types'; -import { EnvironmentAppRouter } from '../EnvironmentAppRouter/EnvironmentAppRouter'; - +import { getAppName } from 'lib/utils/global'; import { SetUp } from 'components/SetUp/SetUp'; import { Loading } from 'ui/Loading/Loading'; import { FourOhFour } from 'ui/ErrorPage/FourOhFour'; import { FourOhThree } from 'ui/ErrorPage/FourOhThree'; +import { EnvironmentAppRouter } from '../EnvironmentAppRouter/EnvironmentAppRouter'; + type DispatchProps = ReturnType; type StateProps = ReturnType; type IEnvironmentsRouterProps = DispatchProps & @@ -39,7 +40,7 @@ interface IEnvironmentsRouterState { const blank: React.FunctionComponent = () => { const message = window.NO_ENVIRONMENT_MESSAGE ?? - 'No Environment Available. Please contact Querybook Admin for more info.'; + `No Environment Available. Please contact ${getAppName()} Admin for more info.`; return
{message}
; }; diff --git a/querybook/webapp/components/Info/Tours.tsx b/querybook/webapp/components/Info/Tours.tsx index ea52d4826..165837bb9 100644 --- a/querybook/webapp/components/Info/Tours.tsx +++ b/querybook/webapp/components/Info/Tours.tsx @@ -7,6 +7,7 @@ import { Dispatch, IStoreState } from 'redux/store/types'; import { queryEngineSelector } from 'redux/queryEngine/selector'; import history from 'lib/router-history'; +import { getAppName } from 'lib/utils/global'; import { getQueryEngineId } from 'lib/utils'; import { navigateWithinEnv } from 'lib/utils/query-string'; @@ -41,17 +42,17 @@ export const Tours: React.FunctionComponent = () => { return (
- Welcome to the Querybook Tutorial! + Welcome to the {getAppName()} Tutorial! Click on a card to start the tutorial.
navigateWithinEnv('/?tour=true')} > - General overview of Querybook functionalities + General overview of {getAppName()} functionalities Overview of DataDoc functionalities diff --git a/querybook/webapp/components/InfoMenuButton/QuerybookVersion.tsx b/querybook/webapp/components/InfoMenuButton/QuerybookVersion.tsx index d40bd1ff3..68d9269f5 100644 --- a/querybook/webapp/components/InfoMenuButton/QuerybookVersion.tsx +++ b/querybook/webapp/components/InfoMenuButton/QuerybookVersion.tsx @@ -1,17 +1,4 @@ -import React, { useEffect } from 'react'; -import { useGlobalState } from 'hooks/redux/useGlobalState'; -import ds from 'lib/datasource'; +import React from 'react'; +import { getAppVersion } from 'lib/utils/global'; -export const QuerybookVersion: React.FC = () => { - const [version, setVersion] = useGlobalState('querybookVersion', null); - - useEffect(() => { - if (version == null) { - ds.fetch(`/version/`).then(({ data }) => { - setVersion(data); - }); - } - }, []); - - return {version}; -}; +export const QuerybookVersion: React.FC = () => {getAppVersion()}; diff --git a/querybook/webapp/components/QueryEngineStatusViewer/QueryEngineStatusViewer.tsx b/querybook/webapp/components/QueryEngineStatusViewer/QueryEngineStatusViewer.tsx index f36f4b679..7636c6a98 100644 --- a/querybook/webapp/components/QueryEngineStatusViewer/QueryEngineStatusViewer.tsx +++ b/querybook/webapp/components/QueryEngineStatusViewer/QueryEngineStatusViewer.tsx @@ -2,6 +2,8 @@ import React, { useCallback, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { titleize } from 'lib/utils'; +import { getAppName } from 'lib/utils/global'; + import { generateFormattedDate } from 'lib/utils/datetime'; import { fetchSystemStatus } from 'redux/queryEngine/action'; import { IStoreState } from 'redux/store/types'; @@ -78,7 +80,7 @@ export const QueryEngineStatusViewer: React.FC = ({ engineId }) => { infoMessage = `Your query may take longer than expected.`; } else if (data.status === QueryEngineStatus.ERROR) { messageType = 'error'; - infoTitle = 'Querybook cannot connect to engine.'; + infoTitle = `${getAppName()} cannot connect to engine.`; infoMessage = 'Running queries on this engine will most likely result in failure.'; } diff --git a/querybook/webapp/components/QueryExecution/QueryError.tsx b/querybook/webapp/components/QueryExecution/QueryError.tsx index c2870ad51..c7a3337e9 100644 --- a/querybook/webapp/components/QueryExecution/QueryError.tsx +++ b/querybook/webapp/components/QueryExecution/QueryError.tsx @@ -14,19 +14,20 @@ import { } from 'lib/sql-helper/sql-lexer'; import { IQueryEngine } from 'const/queryEngine'; +import { getAppName } from 'lib/utils/global'; import { fetchQueryError } from 'redux/queryExecutions/action'; import { IStoreState } from 'redux/store/types'; +import { queryEngineByIdEnvSelector } from 'redux/queryEngine/selector'; -import { ErrorSuggestion } from '../DataDocStatementExecution/ErrorSuggestion'; -import { ExecutedQueryCell, IHighlightRange } from './ExecutedQueryCell'; +import { ErrorSuggestion } from 'components/DataDocStatementExecution/ErrorSuggestion'; import { Icon } from 'ui/Icon/Icon'; import { Message } from 'ui/Message/Message'; import { ShowMoreText } from 'ui/ShowMoreText/ShowMoreText'; import { Tabs } from 'ui/Tabs/Tabs'; import { Loader } from 'ui/Loader/Loader'; +import { ExecutedQueryCell, IHighlightRange } from './ExecutedQueryCell'; import './QueryError.scss'; -import { queryEngineByIdEnvSelector } from 'redux/queryEngine/selector'; interface IProps { queryError: IQueryError; @@ -36,7 +37,7 @@ interface IProps { } const queryErrorTypeToString: Record = { - [QueryExecutionErrorType.INTERNAL]: 'Error from Querybook worker', + [QueryExecutionErrorType.INTERNAL]: `Error from ${getAppName()} worker`, [QueryExecutionErrorType.ENGINE]: 'Error from Query Engine', [QueryExecutionErrorType.SYNTAX]: 'Syntax Error', }; diff --git a/querybook/webapp/components/SetUp/SetUp.tsx b/querybook/webapp/components/SetUp/SetUp.tsx index df91bbc93..678144849 100644 --- a/querybook/webapp/components/SetUp/SetUp.tsx +++ b/querybook/webapp/components/SetUp/SetUp.tsx @@ -3,6 +3,7 @@ import { useDispatch } from 'react-redux'; import ds from 'lib/datasource'; import history from 'lib/router-history'; +import { getAppName } from 'lib/utils/global'; import * as EnvironmentActions from 'redux/environment/action'; import { Card } from 'ui/Card/Card'; @@ -33,7 +34,7 @@ export const SetUp: React.FunctionComponent = () => { return (
-
Welcome to Querybook!
+
Welcome to ${getAppName()}!
{demoLoading ? ( diff --git a/querybook/webapp/components/UIGuide/QuerybookSidebarUIGuide.tsx b/querybook/webapp/components/UIGuide/QuerybookSidebarUIGuide.tsx index 43a5c3ba7..7b0938de4 100644 --- a/querybook/webapp/components/UIGuide/QuerybookSidebarUIGuide.tsx +++ b/querybook/webapp/components/UIGuide/QuerybookSidebarUIGuide.tsx @@ -1,11 +1,14 @@ import React from 'react'; import Tour, { ReactourStep } from 'reactour'; import { useLocation } from 'react-router-dom'; + +import { getQueryString } from 'lib/utils/query-string'; +import { getAppName } from 'lib/utils/global'; + import { Button } from 'ui/Button/Button'; import { Icon } from 'ui/Icon/Icon'; import { Title } from 'ui/Title/Title'; import { Link } from 'ui/Link/Link'; -import { getQueryString } from 'lib/utils/query-string'; function getQuerybookSidebarTourSteps() { const hasEnvironmentTopbar = !!document.querySelector('.EnvironmentTopbar'); @@ -174,11 +177,12 @@ function getQuerybookSidebarTourSteps() { content: ({ goTo }) => ( <>

- That's it! Please send your suggestions to improve the - product on  + That's it! Thanks for using {getAppName()}. Please send + your suggestions to improve the product on  GitHub + .


@@ -225,7 +229,7 @@ export const QuerybookSidebarUIGuide: React.FC = () => { borderless inverted > - Querybook UI Tutorial + UI Tutorial any; @@ -58,8 +59,8 @@ export class UnauthPage extends React.Component<

- NOTE: This signup/login flow for Querybook is only for - people who wants to temporarily try out Querybook. + NOTE: This signup/login flow is only for people who + wants to temporarily try out Querybook.

The user name and password (as salted hash) information @@ -76,6 +77,9 @@ export class UnauthPage extends React.Component< return (

+
+ +
{querybookWarningMessage} {showSignUp && ( diff --git a/querybook/webapp/const/chartColors.ts b/querybook/webapp/const/chartColors.ts index e72a982f7..19aa36793 100644 --- a/querybook/webapp/const/chartColors.ts +++ b/querybook/webapp/const/chartColors.ts @@ -1,5 +1,5 @@ export const colorPalette = [ - '#35B5BB', // dh blue + '#35B5BB', // blue '#ff3975', // pink '#bfbfbf', // grey '#ffca00', // gold @@ -20,7 +20,7 @@ export const colorPalette = [ '#b7652b', // choco ]; export const colorPaletteNames = [ - 'querybook blue', + 'blue', 'pink', 'grey', 'gold', diff --git a/querybook/webapp/hooks/useForwardedRef.ts b/querybook/webapp/hooks/useForwardedRef.ts index 9db8770dc..6a768727e 100644 --- a/querybook/webapp/hooks/useForwardedRef.ts +++ b/querybook/webapp/hooks/useForwardedRef.ts @@ -1,6 +1,6 @@ // https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd -import { useRef, useEffect } from 'react'; +import React, { useRef, useEffect } from 'react'; export function useForwardedRef(ref: React.Ref) { const targetRef = useRef(null); diff --git a/querybook/webapp/lib/querybookUI.ts b/querybook/webapp/lib/querybookUI.ts index c94a2652d..aca2ae419 100644 --- a/querybook/webapp/lib/querybookUI.ts +++ b/querybook/webapp/lib/querybookUI.ts @@ -1,8 +1,8 @@ import { IConfirmationMessageProps } from 'components/ConfirmationManager/ConfirmationMessage'; - -import { reduxStore } from 'redux/store'; import * as querybookUIActions from 'redux/querybookUI/action'; +import { reduxStore } from 'redux/store'; import { Dispatch } from 'redux/store/types'; +import { getAppName } from './utils/global'; export function sendConfirm(props: IConfirmationMessageProps) { (reduxStore.dispatch as Dispatch)( @@ -70,10 +70,11 @@ export function setSessionExpired() { } export function setBrowserTitle(title = '', withSuffix = true) { + const appName = getAppName(); const formattedTitle = withSuffix ? title - ? title + ' - Querybook' - : 'Querybook' + ? title + ' - ' + appName + : appName : title; if (document.title !== formattedTitle) { document.title = formattedTitle; diff --git a/querybook/webapp/lib/utils/global.ts b/querybook/webapp/lib/utils/global.ts new file mode 100644 index 000000000..3a187e0bc --- /dev/null +++ b/querybook/webapp/lib/utils/global.ts @@ -0,0 +1,19 @@ +/** + * Make sure access to pre-defined (webpack) variables are + * sourced from here in case they get changed or needs to + * be mocked for testing + */ + +/** + * Get the version defined by package.json + */ +export function getAppVersion() { + return __VERSION__; +} + +/** + * Get the name of the app, defaults to 'Querybook' + */ +export function getAppName() { + return __APPNAME__; +} diff --git a/querybook/webapp/stylesheets/_variables.scss b/querybook/webapp/stylesheets/_variables.scss index 5e98fb32e..c66b4e7da 100644 --- a/querybook/webapp/stylesheets/_variables.scss +++ b/querybook/webapp/stylesheets/_variables.scss @@ -116,8 +116,6 @@ body { --red-highlight: rgba(189, 8, 28, 0.5); --text-highlight: rgba(255, 255, 0, 0.5); - --mix-blend-mode: multiply; - // Type --family-sans-serif: 'Avenir Next', 'Avenir', 'Helvetica Neue', 'Arial', sans-serif; @@ -200,7 +198,6 @@ body.dark-theme { --text-select-bg-color: var(--color-transparent-dark); --scroll-bar-color: var(--color-primary-3); - --mix-blend-mode: lighten; --text-highlight: rgba(52, 101, 127, 0.5); @@ -235,7 +232,6 @@ body.night-theme { --text-select-bg-color: var(--color-transparent-dark); --scroll-bar-color: var(--color-primary-3); - --mix-blend-mode: lighten; font-weight: 500; @@ -277,7 +273,6 @@ body.lush-theme { --text-select-bg-color: var(--color-transparent-dark); --scroll-bar-color: var(--color-primary-3); - --mix-blend-mode: lighten; font-weight: 500; diff --git a/querybook/webapp/types.d.ts b/querybook/webapp/types.d.ts new file mode 100644 index 000000000..af0035c75 --- /dev/null +++ b/querybook/webapp/types.d.ts @@ -0,0 +1,32 @@ +import { reduxStore } from 'redux/store'; +import type { + IColumnStatsAnalyzer, + IColumnDetector, + IColumnTransformer, +} from 'lib/query-result/types'; + +declare global { + /* eslint-disable @typescript-eslint/naming-convention */ + interface Window { + reduxStore?: typeof reduxStore; + receiveChildMessage?: () => void; + + // Web Plugin Variables + NO_ENVIRONMENT_MESSAGE?: string; + CUSTOM_LANDING_PAGE?: { + // Two modes of custom landing page + // replace: replace the entire landing page with custom content + // not specified: add the custom content to the middle of the + // landing page + mode?: 'replace'; + renderer: () => React.ReactElement; + }; + CUSTOM_COLUMN_STATS_ANALYZERS?: IColumnStatsAnalyzer[]; + CUSTOM_COLUMN_DETECTORS?: IColumnDetector[]; + CUSTOM_COLUMN_TRANSFORMERS?: IColumnTransformer[]; + } + + // Injected via Webpack + const __VERSION__: string; + const __APPNAME__: string; +} diff --git a/querybook/webapp/ui/Menu/ListMenu.tsx b/querybook/webapp/ui/Menu/ListMenu.tsx index 263f451da..7bdcfd492 100644 --- a/querybook/webapp/ui/Menu/ListMenu.tsx +++ b/querybook/webapp/ui/Menu/ListMenu.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import React from 'react'; import classNames from 'classnames'; import { calculateTooltipSize } from 'lib/utils'; diff --git a/querybook/webapp/ui/QuerybookLogo/QuerybookLogo.tsx b/querybook/webapp/ui/QuerybookLogo/QuerybookLogo.tsx index 2c71e9c1f..fe2cc8e3c 100644 --- a/querybook/webapp/ui/QuerybookLogo/QuerybookLogo.tsx +++ b/querybook/webapp/ui/QuerybookLogo/QuerybookLogo.tsx @@ -1,34 +1,36 @@ import React from 'react'; import styled from 'styled-components'; +import { getAppName } from 'lib/utils/global'; const StyledQuerybookLogo = styled.span` display: flex; align-items: center; justify-content: center; + font-size: ${({ size }) => `${size}rem`}; .querybook-brandmark { width: ${({ size }) => `${size * 1.3}rem`}; height: ${({ size }) => `${size * 1.3}rem`}; } .querybook-wordmark { - align-items: center; - font-size: ${({ size }) => `${size}rem`}; font-weight: 700; + letter-spacing: -0.05em; - padding: 0px; - user-select: none; + padding-right: 0.05em; - .querybook-title-first-part { - color: var(--color-red); - } + user-select: none; + color: var(--color-accent); - .querybook-title-last-part { - color: var(--color-blue); - position: relative; - left: -0.18em; - mix-blend-mode: var(--mix-blend-mode); - } + background: -webkit-linear-gradient( + 45deg, + var(--color-accent-text), + var(--color-accent), + var(--color-accent-bg) + ); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; } `; @@ -43,9 +45,6 @@ export const QuerybookLogo: React.FC<{ src={'/static/favicon/favicon.ico'} /> )} - - Query - book - + {getAppName()} ); diff --git a/webpack.config.js b/webpack.config.js index e21edaea9..649ab5d33 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -96,6 +96,8 @@ module.exports = (env) => { entry.custom = customScriptPath; } + const appName = process.env.QUERYBOOK_APPNAME ?? 'Querybook'; + return { entry, mode: PROD ? 'production' : 'development', @@ -190,6 +192,10 @@ module.exports = (env) => { plugins: [ new CleanObsoleteChunks(), new CleanWebpackPlugin([BUILD_DIR]), + new webpack.DefinePlugin({ + __VERSION__: JSON.stringify(require('./package.json').version), + __APPNAME__: JSON.stringify(appName), + }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new webpack.LoaderOptionsPlugin({ options: { @@ -205,7 +211,7 @@ module.exports = (env) => { filename: '[name].[contenthash:4].css', }), new HtmlWebpackPlugin({ - title: 'Querybook', + title: appName, template: './querybook/webapp/index.html', chunks: ['react_hot_loader', 'vendor'] .concat(entry.custom ? ['custom'] : [])