mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(core) Add GRIST_UI_FEATURES env variable
Summary: Tutorials are now hidden by default in grist-core and grist-ee, and can be re-enabled via a new env variable, GRIST_UI_FEATURES, which accepts a comma-separated list of UI features to enable. Test Plan: Browser tests. Reviewers: jarek Reviewed By: jarek Subscribers: jarek Differential Revision: https://phab.getgrist.com/D3885
This commit is contained in:
		
							parent
							
								
									1e873b4203
								
							
						
					
					
						commit
						f18bb3e39d
					
				
							
								
								
									
										17
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								README.md
									
									
									
									
									
								
							| @ -43,8 +43,8 @@ Here are some specific feature highlights of Grist: | ||||
|     - Any tool that can read SQLite can read numeric and text data from a Grist file. | ||||
|     - Great format for [backups](https://support.getgrist.com/exports/#backing-up-an-entire-document) that you can be confident you can restore in full. | ||||
|     - Great format for moving between different hosts. | ||||
| 	- Can be displayed on a static website with [grist-static](https://github.com/gristlabs/grist-static). | ||||
| 	- There's a self-contained desktop app available for viewing and editing: [grist-electron](https://github.com/gristlabs/grist-electron). | ||||
|   - Can be displayed on a static website with [grist-static](https://github.com/gristlabs/grist-static). | ||||
|   - There's a self-contained desktop app available for viewing and editing: [grist-electron](https://github.com/gristlabs/grist-electron). | ||||
|   * Convenient editing and formatting features. | ||||
|     - Choices and [choice lists](https://support.getgrist.com/col-types/#choice-list-columns), for adding colorful tags to records without fuss. | ||||
|     - [References](https://support.getgrist.com/col-refs/#creating-a-new-reference-list-column) and reference lists, for cross-referencing records in other tables. | ||||
| @ -78,7 +78,7 @@ Here are some specific feature highlights of Grist: | ||||
|       [gVisor](https://github.com/google/gvisor) sandboxing at the individual | ||||
|       document level. | ||||
|     - On OSX, you can use native sandboxing. | ||||
| 	- On any OS, including Windows, you can use a wasm-based sandbox. | ||||
|   - On any OS, including Windows, you can use a wasm-based sandbox. | ||||
|   * Translated to many languages. | ||||
| 
 | ||||
| If you are curious about where Grist is going heading, | ||||
| @ -93,7 +93,7 @@ If you just want a quick demo of Grist: | ||||
| 
 | ||||
|   * You can try Grist out at the hosted service run | ||||
|     by Grist Labs at [docs.getgrist.com](https://docs.getgrist.com) | ||||
| 	(no registration needed). | ||||
|   (no registration needed). | ||||
|   * Or you can see an experimental fully in-browser build of Grist | ||||
|     at [gristlabs.github.io/grist-static](https://gristlabs.github.io/grist-static/). | ||||
|   * Or you can download Grist as a desktop app from [github.com/gristlabs/grist-electron](https://github.com/gristlabs/grist-electron). | ||||
| @ -227,9 +227,9 @@ Grist benefits its users: | ||||
| ## Sponsors | ||||
| 
 | ||||
| <p align="center"> | ||||
| 	<a href="https://www.dotphoton.com/"> | ||||
| 	 <img width="11%" src="https://user-images.githubusercontent.com/11277225/228914729-ae581352-b37a-4ca8-b220-b1463dd1ade0.png" /> | ||||
| 	</a> | ||||
|   <a href="https://www.dotphoton.com/"> | ||||
|     <img width="11%" src="https://user-images.githubusercontent.com/11277225/228914729-ae581352-b37a-4ca8-b220-b1463dd1ade0.png" /> | ||||
|   </a> | ||||
| </p> | ||||
| 
 | ||||
| ## Reviews | ||||
| @ -260,7 +260,7 @@ GRIST_DEFAULT_PRODUCT  | if set, this controls enabled features and limits of ne | ||||
| GRIST_DEFAULT_LOCALE  | Locale to use as fallback when Grist cannot honour the browser locale. | ||||
| GRIST_DOMAIN        | in hosted Grist, Grist is served from subdomains of this domain.  Defaults to "getgrist.com". | ||||
| GRIST_EXPERIMENTAL_PLUGINS | enables experimental plugins | ||||
| GRIST_HIDE_UI_ELEMENTS | comma-separated list of parts of the UI to hide. Allowed names of parts: `helpCenter,billing,templates,multiSite,multiAccounts,sendToDrive` | ||||
| GRIST_HIDE_UI_ELEMENTS | comma-separated list of UI features to disable. Allowed names of parts: `helpCenter,billing,templates,multiSite,multiAccounts,sendToDrive,tutorials`. If a part also exists in GRIST_UI_FEATURES, it will still be disabled. | ||||
| GRIST_HOME_INCLUDE_STATIC | if set, home server also serves static resources | ||||
| GRIST_HOST          | hostname to use when listening on a port. | ||||
| GRIST_ID_PREFIX | for subdomains of form o-*, expect or produce o-${GRIST_ID_PREFIX}*. | ||||
| @ -286,6 +286,7 @@ GRIST_SUPPORT_ANON | if set to 'true', show UI for anonymous access (not shown b | ||||
| GRIST_SUPPORT_EMAIL | if set, give a user with the specified email support powers. The main extra power is the ability to share sites, workspaces, and docs with all users in a listed way. | ||||
| GRIST_THROTTLE_CPU | if set, CPU throttling is enabled | ||||
| GRIST_USER_ROOT     | an extra path to look for plugins in. | ||||
| GRIST_UI_FEATURES | comma-separated list of UI features to enable. Allowed names of parts: `helpCenter,billing,templates,multiSite,multiAccounts,sendToDrive,tutorials`. If a part also exists in GRIST_HIDE_UI_ELEMENTS, it won't be enabled. | ||||
| GRIST_WIDGET_LIST_URL | a url pointing to a widget manifest | ||||
| COOKIE_MAX_AGE      | session cookie max age, defaults to 90 days; can be set to "none" to make it a session cookie | ||||
| HOME_PORT           | port number to listen on for REST API server; if set to "share", add API endpoints to regular grist port. | ||||
|  | ||||
| @ -9,7 +9,7 @@ import {primaryButton} from 'app/client/ui2018/buttons'; | ||||
| import {mediaDeviceNotSmall, testId, theme, vars} from 'app/client/ui2018/cssVars'; | ||||
| import {icon} from 'app/client/ui2018/icons'; | ||||
| import {menu, menuDivider, menuItem, menuItemLink, menuSubHeader} from 'app/client/ui2018/menus'; | ||||
| import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; | ||||
| import {FullUser} from 'app/common/LoginSessionAPI'; | ||||
| import * as roles from 'app/common/roles'; | ||||
| import {Disposable, dom, DomElementArg, styled} from 'grainjs'; | ||||
| @ -107,7 +107,7 @@ export class AccountWidget extends Disposable { | ||||
| 
 | ||||
|       // In case of a single-org setup, skip all the account-switching UI. We'll also skip the
 | ||||
|       // org-listing UI below.
 | ||||
|       this._appModel.topAppModel.isSingleOrg || shouldHideUiElement("multiAccounts") ? [] : [ | ||||
|       this._appModel.topAppModel.isSingleOrg || !isFeatureEnabled("multiAccounts") ? [] : [ | ||||
|         menuDivider(), | ||||
|         menuSubHeader(dom.text((use) => use(users).length > 1 ? t("Switch Accounts") : t("Accounts"))), | ||||
|         dom.forEach(users, (_user) => { | ||||
|  | ||||
| @ -9,7 +9,7 @@ import {bigBasicButton, cssButton} from 'app/client/ui2018/buttons'; | ||||
| import {testId, theme, vars} from 'app/client/ui2018/cssVars'; | ||||
| import {icon} from 'app/client/ui2018/icons'; | ||||
| import {cssLink} from 'app/client/ui2018/links'; | ||||
| import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; | ||||
| import {FullUser} from 'app/common/LoginSessionAPI'; | ||||
| import * as roles from 'app/common/roles'; | ||||
| import {Computed, dom, DomContents, styled} from 'grainjs'; | ||||
| @ -90,7 +90,7 @@ function makeTeamSiteIntro(homeModel: HomeModel) { | ||||
|       testId('welcome-title') | ||||
|     ), | ||||
|     cssIntroLine(t("Get started by inviting your team and creating your first Grist document.")), | ||||
|     (shouldHideUiElement('helpCenter') ? null : | ||||
|     (!isFeatureEnabled('helpCenter') ? null : | ||||
|       cssIntroLine( | ||||
|         'Learn more in our ', helpCenterLink(), ', or find an expert via our ', sproutsProgram, '.',  // TODO i18n
 | ||||
|         testId('welcome-text') | ||||
| @ -104,7 +104,7 @@ function makePersonalIntro(homeModel: HomeModel, user: FullUser) { | ||||
|   return [ | ||||
|     css.docListHeader(t("Welcome to Grist, {{- name}}!", {name: user.name}), testId('welcome-title')), | ||||
|     cssIntroLine(t("Get started by creating your first Grist document.")), | ||||
|     (shouldHideUiElement('helpCenter') ? null : | ||||
|     (!isFeatureEnabled('helpCenter') ? null : | ||||
|       cssIntroLine(t("Visit our {{link}} to learn more.", { link: helpCenterLink() }), | ||||
|         testId('welcome-text')) | ||||
|     ), | ||||
| @ -118,7 +118,7 @@ function makeAnonIntro(homeModel: HomeModel) { | ||||
|     css.docListHeader(t("Welcome to Grist!"), testId('welcome-title')), | ||||
|     cssIntroLine(t("Get started by exploring templates, or creating your first Grist document.")), | ||||
|     cssIntroLine(t("{{signUp}} to save your work. ", {signUp}), | ||||
|       (shouldHideUiElement('helpCenter') ? null : t("Visit our {{link}} to learn more.", { link: helpCenterLink() })), | ||||
|       (!isFeatureEnabled('helpCenter') ? null : t("Visit our {{link}} to learn more.", { link: helpCenterLink() })), | ||||
|       testId('welcome-text')), | ||||
|     makeCreateButtons(homeModel), | ||||
|   ]; | ||||
| @ -143,7 +143,7 @@ function buildButtons(homeModel: HomeModel, options: { | ||||
|     !options.templates ? null : | ||||
|     cssBtn(cssBtnIcon('FieldTable'), t("Browse Templates"), testId('intro-templates'), | ||||
|       cssButton.cls('-primary'), | ||||
|       dom.hide(shouldHideUiElement("templates")), | ||||
|       dom.show(isFeatureEnabled("templates")), | ||||
|       urlState().setLinkUrl({homePage: 'templates'}), | ||||
|     ), | ||||
|     !options.import ? null : | ||||
|  | ||||
| @ -16,7 +16,7 @@ import {testId, theme} from 'app/client/ui2018/cssVars'; | ||||
| import {icon} from 'app/client/ui2018/icons'; | ||||
| import {menu, menuIcon, menuItem, upgradableMenuItem, upgradeText} from 'app/client/ui2018/menus'; | ||||
| import {confirmModal} from 'app/client/ui2018/modals'; | ||||
| import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; | ||||
| import * as roles from 'app/common/roles'; | ||||
| import {Workspace} from 'app/common/UserAPI'; | ||||
| import {computed, dom, domComputed, DomElementArg, observable, Observable, styled} from 'grainjs'; | ||||
| @ -109,7 +109,7 @@ export function createHomeLeftPane(leftPanelOpen: Observable<boolean>, home: Hom | ||||
|       )), | ||||
|       cssTools( | ||||
|         cssPageEntry( | ||||
|           dom.hide(shouldHideUiElement("templates")), | ||||
|           dom.show(isFeatureEnabled("templates")), | ||||
|           cssPageEntry.cls('-selected', (use) => use(home.currentPage) === "templates"), | ||||
|           cssPageLink(cssPageIcon('Board'), cssLinkText(t("Examples & Templates")), | ||||
|             urlState().setLinkUrl({homePage: "templates"}), | ||||
| @ -125,7 +125,7 @@ export function createHomeLeftPane(leftPanelOpen: Observable<boolean>, home: Hom | ||||
|         ), | ||||
|         cssSpacer(), | ||||
|         cssPageEntry( | ||||
|           dom.hide(shouldHideUiElement("templates")), | ||||
|           dom.show(isFeatureEnabled('tutorials')), | ||||
|           cssPageLink(cssPageIcon('Bookmark'), cssLinkText(t("Tutorial")), | ||||
|             { href: commonUrls.basicTutorial, target: '_blank' }, | ||||
|             testId('dm-basic-tutorial'), | ||||
|  | ||||
| @ -18,7 +18,7 @@ import {makeT} from 'app/client/lib/localization'; | ||||
| import {AppModel} from 'app/client/models/AppModel'; | ||||
| import {testId, theme, vars} from 'app/client/ui2018/cssVars'; | ||||
| import {icon} from 'app/client/ui2018/icons'; | ||||
| import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; | ||||
| import {dom, DomContents, Observable, styled} from 'grainjs'; | ||||
| 
 | ||||
| const t = makeT('LeftPanelCommon'); | ||||
| @ -28,7 +28,7 @@ const t = makeT('LeftPanelCommon'); | ||||
|  * HelpCenter in a new tab. | ||||
|  */ | ||||
| export function createHelpTools(appModel: AppModel): DomContents { | ||||
|   if (shouldHideUiElement("helpCenter")) { | ||||
|   if (!isFeatureEnabled("helpCenter")) { | ||||
|     return []; | ||||
|   } | ||||
|   return cssSplitPageEntry( | ||||
|  | ||||
| @ -10,7 +10,7 @@ import {theme, vars} from 'app/client/ui2018/cssVars'; | ||||
| import {icon} from 'app/client/ui2018/icons'; | ||||
| import {IconName} from "app/client/ui2018/IconList"; | ||||
| import {menuCssClass} from 'app/client/ui2018/menus'; | ||||
| import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; | ||||
| import {dom, makeTestId, styled} from 'grainjs'; | ||||
| import {cssMenu, defaultMenuOptions, IOpenController, setPopupToCreateDom} from 'popweasel'; | ||||
| 
 | ||||
| @ -159,7 +159,7 @@ function buildNotifyDropdown(ctl: IOpenController, notifier: Notifier, appModel: | ||||
|     cssDropdownContent( | ||||
|       cssDropdownHeader( | ||||
|         cssDropdownHeaderTitle(t("Notifications")), | ||||
|         shouldHideUiElement("helpCenter") ? null : | ||||
|         !isFeatureEnabled("helpCenter") ? null : | ||||
|         cssDropdownFeedbackLink( | ||||
|           cssDropdownFeedbackIcon('Feedback'), | ||||
|           t("Give feedback"), | ||||
|  | ||||
| @ -7,7 +7,7 @@ import {YouTubePlayer} from 'app/client/ui/YouTubePlayer'; | ||||
| import {theme} from 'app/client/ui2018/cssVars'; | ||||
| import {icon} from 'app/client/ui2018/icons'; | ||||
| import {cssModalCloseButton, modal} from 'app/client/ui2018/modals'; | ||||
| import {shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {isFeatureEnabled} from 'app/common/gristUrls'; | ||||
| import {dom, makeTestId, styled} from 'grainjs'; | ||||
| 
 | ||||
| const t = makeT('OpenVideoTour'); | ||||
| @ -79,7 +79,7 @@ export function createVideoTourTextButton(): HTMLDivElement { | ||||
|  * Shows the video tour on click. | ||||
|  */ | ||||
| export function createVideoTourToolsButton(): HTMLDivElement | null { | ||||
|   if (shouldHideUiElement('helpCenter')) { return null; } | ||||
|   if (!isFeatureEnabled('helpCenter')) { return null; } | ||||
| 
 | ||||
|   let iconElement: HTMLElement; | ||||
| 
 | ||||
|  | ||||
| @ -11,7 +11,7 @@ import {primaryButton} from 'app/client/ui2018/buttons'; | ||||
| import {mediaXSmall, testId, theme} from 'app/client/ui2018/cssVars'; | ||||
| import {icon} from 'app/client/ui2018/icons'; | ||||
| import {menu, menuAnnotate, menuDivider, menuIcon, menuItem, menuItemLink, menuText} from 'app/client/ui2018/menus'; | ||||
| import {buildUrlId, parseUrlId, shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {buildUrlId, isFeatureEnabled, parseUrlId} from 'app/common/gristUrls'; | ||||
| import * as roles from 'app/common/roles'; | ||||
| import {Document} from 'app/common/UserAPI'; | ||||
| import {dom, DomContents, styled} from 'grainjs'; | ||||
| @ -244,7 +244,7 @@ function menuExports(doc: Document, pageModel: DocPageModel) { | ||||
|       href: pageModel.appModel.api.getDocAPI(doc.id).getDownloadXlsxUrl(), | ||||
|       target: '_blank', download: '' | ||||
|     }, menuIcon('Download'), t("Export XLSX"), testId('tb-share-option')), | ||||
|     (shouldHideUiElement("sendToDrive") ? null : menuItem(() => sendToDrive(doc, pageModel), | ||||
|     (!isFeatureEnabled("sendToDrive") ? null : menuItem(() => sendToDrive(doc, pageModel), | ||||
|       menuIcon('Download'), t("Send to Google Drive"), testId('tb-share-option'))), | ||||
|   ]; | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import {dom, makeTestId, styled} from 'grainjs'; | ||||
| import {getSingleOrg, shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {getSingleOrg, isFeatureEnabled} from 'app/common/gristUrls'; | ||||
| import {getOrgName} from 'app/common/UserAPI'; | ||||
| import {makeT} from 'app/client/lib/localization'; | ||||
| import {AppModel} from 'app/client/models/AppModel'; | ||||
| @ -17,7 +17,7 @@ const testId = makeTestId('test-site-switcher-'); | ||||
|  */ | ||||
| export function maybeAddSiteSwitcherSection(appModel: AppModel) { | ||||
|   const orgs = appModel.topAppModel.orgs; | ||||
|   return dom.maybe((use) => use(orgs).length > 0 && !getSingleOrg() && !shouldHideUiElement("multiSite"), () => [ | ||||
|   return dom.maybe((use) => use(orgs).length > 0 && !getSingleOrg() && isFeatureEnabled("multiSite"), () => [ | ||||
|     menuDivider(), | ||||
|     buildSiteSwitcher(appModel), | ||||
|   ]); | ||||
|  | ||||
| @ -2,7 +2,7 @@ import {AppModel} from 'app/client/models/AppModel'; | ||||
| import {bigPrimaryButton} from 'app/client/ui2018/buttons'; | ||||
| import {isNarrowScreenObs, theme} from 'app/client/ui2018/cssVars'; | ||||
| import {icon} from 'app/client/ui2018/icons'; | ||||
| import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls'; | ||||
| import {commonUrls, isFeatureEnabled} from 'app/common/gristUrls'; | ||||
| import {Computed, dom, IDisposableOwner, makeTestId, styled} from 'grainjs'; | ||||
| 
 | ||||
| const testId = makeTestId('test-tutorial-card-'); | ||||
| @ -12,7 +12,7 @@ interface Options { | ||||
| } | ||||
| 
 | ||||
| export function buildTutorialCard(owner: IDisposableOwner, options: Options) { | ||||
|   if (shouldHideUiElement('templates')) { return null; } | ||||
|   if (!isFeatureEnabled('tutorials')) { return null; } | ||||
| 
 | ||||
|   const {app} = options; | ||||
|   const dismissed = app.dismissedPopup('tutorialFirstCard'); | ||||
|  | ||||
| @ -584,8 +584,8 @@ export interface GristLoadConfig { | ||||
| 
 | ||||
|   activation?: Activation; | ||||
| 
 | ||||
|   // Parts of the UI to hide
 | ||||
|   hideUiElements?: IHideableUiElement[]; | ||||
|   // List of enabled features.
 | ||||
|   features?: IFeature[]; | ||||
| 
 | ||||
|   // String to append to the end of the HTML document.title
 | ||||
|   pageTitleSuffix?: string; | ||||
| @ -612,12 +612,19 @@ export interface GristLoadConfig { | ||||
|   userLocale?: string; | ||||
| } | ||||
| 
 | ||||
| export const HideableUiElements = StringUnion("helpCenter", "billing", "templates", "multiSite", "multiAccounts", | ||||
| "sendToDrive"); | ||||
| export type IHideableUiElement = typeof HideableUiElements.type; | ||||
| export const Features = StringUnion( | ||||
|   "helpCenter", | ||||
|   "billing", | ||||
|   "templates", | ||||
|   "multiSite", | ||||
|   "multiAccounts", | ||||
|   "sendToDrive", | ||||
|   "tutorials", | ||||
| ); | ||||
| export type IFeature = typeof Features.type; | ||||
| 
 | ||||
| export function shouldHideUiElement(elem: IHideableUiElement): boolean { | ||||
|   return (getGristConfig().hideUiElements || []).includes(elem); | ||||
| export function isFeatureEnabled(feature: IFeature): boolean { | ||||
|   return (getGristConfig().features || []).includes(feature); | ||||
| } | ||||
| 
 | ||||
| export function getPageTitleSuffix(config?: GristLoadConfig) { | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import {getPageTitleSuffix, GristLoadConfig, HideableUiElements, IHideableUiElement} from 'app/common/gristUrls'; | ||||
| import {Features, getPageTitleSuffix, GristLoadConfig, IFeature} from 'app/common/gristUrls'; | ||||
| import {isAffirmative} from 'app/common/gutil'; | ||||
| import {getTagManagerSnippet} from 'app/common/tagManager'; | ||||
| import {Document} from 'app/common/UserAPI'; | ||||
| @ -13,6 +13,7 @@ import * as fse from 'fs-extra'; | ||||
| import jsesc from 'jsesc'; | ||||
| import * as handlebars from 'handlebars'; | ||||
| import * as path from 'path'; | ||||
| import difference = require('lodash/difference'); | ||||
| 
 | ||||
| const translate = (req: express.Request, key: string, args?: any) => req.t(`sendAppPage.${key}`, args); | ||||
| 
 | ||||
| @ -47,7 +48,7 @@ export function makeGristConfig(homeUrl: string|null, extra: Partial<GristLoadCo | ||||
|     pathOnly, | ||||
|     supportAnon: shouldSupportAnon(), | ||||
|     supportEngines: getSupportedEngineChoices(), | ||||
|     hideUiElements: getHiddenUiElements(), | ||||
|     features: getFeatures(), | ||||
|     pageTitleSuffix: configuredPageTitleSuffix(), | ||||
|     pluginUrl, | ||||
|     stripeAPIKey: process.env.STRIPE_PUBLIC_API_KEY, | ||||
| @ -143,12 +144,10 @@ function shouldSupportAnon() { | ||||
|   return process.env.GRIST_SUPPORT_ANON === "true"; | ||||
| } | ||||
| 
 | ||||
| function getHiddenUiElements(): IHideableUiElement[] { | ||||
|   const str = process.env.GRIST_HIDE_UI_ELEMENTS; | ||||
|   if (!str) { | ||||
|     return []; | ||||
|   } | ||||
|   return HideableUiElements.checkAll(str.split(",")); | ||||
| function getFeatures(): IFeature[] { | ||||
|   const disabledFeatures = process.env.GRIST_HIDE_UI_ELEMENTS?.split(',') ?? []; | ||||
|   const enabledFeatures = process.env.GRIST_UI_FEATURES?.split(',') ?? Features.values; | ||||
|   return Features.checkAll(difference(enabledFeatures, disabledFeatures)); | ||||
| } | ||||
| 
 | ||||
| function configuredPageTitleSuffix() { | ||||
|  | ||||
| @ -30,6 +30,8 @@ if (!process.env.GRIST_SINGLE_ORG) { | ||||
|   setDefaultEnv('GRIST_ORG_IN_PATH', 'true'); | ||||
| } | ||||
| 
 | ||||
| setDefaultEnv('GRIST_UI_FEATURES', 'helpCenter,billing,templates,multiSite,multiAccounts,sendToDrive'); | ||||
| 
 | ||||
| import {updateDb} from 'app/server/lib/dbUtils'; | ||||
| import {main as mergedServerMain} from 'app/server/mergedServerMain'; | ||||
| import * as fse from 'fs-extra'; | ||||
|  | ||||
							
								
								
									
										51
									
								
								test/nbrowser/Features.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								test/nbrowser/Features.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| import { assert, driver } from 'mocha-webdriver'; | ||||
| import * as gu from 'test/nbrowser/gristUtils'; | ||||
| import { server, setupTestSuite } from 'test/nbrowser/testUtils'; | ||||
| import { EnvironmentSnapshot } from 'test/server/testUtils'; | ||||
| 
 | ||||
| describe('Features', function () { | ||||
|   this.timeout(20000); | ||||
|   setupTestSuite(); | ||||
| 
 | ||||
|   let session: gu.Session; | ||||
|   let oldEnv: EnvironmentSnapshot; | ||||
| 
 | ||||
|   before(async function () { | ||||
|     oldEnv = new EnvironmentSnapshot(); | ||||
|     session = await gu.session().teamSite.login(); | ||||
|   }); | ||||
| 
 | ||||
|   after(async function () { | ||||
|     oldEnv.restore(); | ||||
|     await server.restart(); | ||||
|   }); | ||||
| 
 | ||||
|   it('can be enabled with the GRIST_UI_FEATURES env variable', async function () { | ||||
|     process.env.GRIST_UI_FEATURES = 'helpCenter,templates'; | ||||
|     await server.restart(); | ||||
|     await session.loadDocMenu('/'); | ||||
|     assert.isTrue(await driver.find('.test-dm-templates-page').isDisplayed()); | ||||
|     assert.isTrue(await driver.find('.test-left-feedback').isDisplayed()); | ||||
|     assert.isFalse(await driver.find('.test-dm-basic-tutorial').isDisplayed()); | ||||
|   }); | ||||
| 
 | ||||
|   it('can be disabled with the GRIST_HIDE_UI_ELEMENTS env variable', async function () { | ||||
|     delete process.env.GRIST_UI_FEATURES; | ||||
|     process.env.GRIST_HIDE_UI_ELEMENTS = 'templates'; | ||||
|     await server.restart(); | ||||
|     await session.loadDocMenu('/'); | ||||
|     assert.isTrue(await driver.find('.test-left-feedback').isDisplayed()); | ||||
|     assert.isTrue(await driver.find('.test-dm-basic-tutorial').isDisplayed()); | ||||
|     assert.isFalse(await driver.find('.test-dm-templates-page').isDisplayed()); | ||||
|   }); | ||||
| 
 | ||||
|   it('that are disabled take precedence if also enabled', async function () { | ||||
|     process.env.GRIST_UI_FEATURES = 'tutorials,templates'; | ||||
|     process.env.GRIST_HIDE_UI_ELEMENTS = 'helpCenter,templates'; | ||||
|     await server.restart(); | ||||
|     await session.loadDocMenu('/'); | ||||
|     assert.isTrue(await driver.find('.test-dm-basic-tutorial').isDisplayed()); | ||||
|     assert.isFalse(await driver.find('.test-left-feedback').isPresent()); | ||||
|     assert.isFalse(await driver.find('.test-dm-templates-page').isDisplayed()); | ||||
|   }); | ||||
| }); | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user