mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(core) Adding empty workspace intro
Summary: Adding title and buttons for empty workspace screen. Test Plan: Updated Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D3622
This commit is contained in:
		
							parent
							
								
									b9441cf8fd
								
							
						
					
					
						commit
						81c623109f
					
				@ -9,7 +9,7 @@ import {docUrl, urlState} from 'app/client/models/gristUrlState';
 | 
			
		||||
import {getTimeFromNow, HomeModel, makeLocalViewSettings, ViewSettings} from 'app/client/models/HomeModel';
 | 
			
		||||
import {getWorkspaceInfo, workspaceName} from 'app/client/models/WorkspaceInfo';
 | 
			
		||||
import * as css from 'app/client/ui/DocMenuCss';
 | 
			
		||||
import {buildHomeIntro} from 'app/client/ui/HomeIntro';
 | 
			
		||||
import {buildHomeIntro, buildWorkspaceIntro} from 'app/client/ui/HomeIntro';
 | 
			
		||||
import {buildUpgradeButton} from 'app/client/ui/ProductUpgrades';
 | 
			
		||||
import {buildPinnedDoc, createPinnedDocs} from 'app/client/ui/PinnedDocs';
 | 
			
		||||
import {shadowScroll} from 'app/client/ui/shadowScroll';
 | 
			
		||||
@ -134,11 +134,13 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
 | 
			
		||||
                  dom('div',
 | 
			
		||||
                    buildAllTemplates(home, home.templateWorkspaces, viewSettings)
 | 
			
		||||
                  ) :
 | 
			
		||||
                  workspace && !workspace.isSupportWorkspace ?
 | 
			
		||||
                  workspace && !workspace.isSupportWorkspace && workspace.docs?.length ?
 | 
			
		||||
                    css.docBlock(
 | 
			
		||||
                      buildWorkspaceDocBlock(home, workspace, flashDocId, viewSettings),
 | 
			
		||||
                      testId('doc-block')
 | 
			
		||||
                    ) :
 | 
			
		||||
                  workspace && !workspace.isSupportWorkspace && workspace.docs?.length === 0 ?
 | 
			
		||||
                  buildWorkspaceIntro(home) :
 | 
			
		||||
                  css.docBlock('Workspace not found')
 | 
			
		||||
              )
 | 
			
		||||
            ]),
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,25 @@ export function buildHomeIntro(homeModel: HomeModel): DomContents {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function buildWorkspaceIntro(homeModel: HomeModel): DomContents {
 | 
			
		||||
  const isViewer = homeModel.currentWS.get()?.access === roles.VIEWER;
 | 
			
		||||
  const isAnonym = !homeModel.app.currentValidUser;
 | 
			
		||||
  const emptyLine = cssIntroLine(testId('empty-workspace-info'), "This workspace is empty.");
 | 
			
		||||
  if (isAnonym || isViewer) {
 | 
			
		||||
    return emptyLine;
 | 
			
		||||
  } else {
 | 
			
		||||
    return [
 | 
			
		||||
      emptyLine,
 | 
			
		||||
      buildButtons(homeModel, {
 | 
			
		||||
        invite: false,
 | 
			
		||||
        templates: false,
 | 
			
		||||
        import: true,
 | 
			
		||||
        empty: true
 | 
			
		||||
      })
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function makeViewerTeamSiteIntro(homeModel: HomeModel) {
 | 
			
		||||
  const personalOrg = Computed.create(null, use => use(homeModel.app.topAppModel.orgs).find(o => o.owner));
 | 
			
		||||
  const docLink = (dom.maybe(personalOrg, org => {
 | 
			
		||||
@ -105,31 +124,46 @@ function helpCenterLink() {
 | 
			
		||||
  return cssLink({href: commonUrls.help, target: '_blank'}, cssInlineIcon('Help'), 'Help Center');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function makeCreateButtons(homeModel: HomeModel) {
 | 
			
		||||
  const canManageTeam = homeModel.app.isTeamSite &&
 | 
			
		||||
    roles.canEditAccess(homeModel.app.currentOrg?.access || null);
 | 
			
		||||
function buildButtons(homeModel: HomeModel, options: {
 | 
			
		||||
  invite: boolean,
 | 
			
		||||
  templates: boolean,
 | 
			
		||||
  import: boolean,
 | 
			
		||||
  empty: boolean,
 | 
			
		||||
}) {
 | 
			
		||||
  return cssBtnGroup(
 | 
			
		||||
    (canManageTeam ?
 | 
			
		||||
    !options.invite ? null :
 | 
			
		||||
    cssBtn(cssBtnIcon('Help'), 'Invite Team Members', testId('intro-invite'),
 | 
			
		||||
      cssButton.cls('-primary'),
 | 
			
		||||
      dom.on('click', () => manageTeamUsersApp(homeModel.app)),
 | 
			
		||||
      ) :
 | 
			
		||||
    ),
 | 
			
		||||
    !options.templates ? null :
 | 
			
		||||
    cssBtn(cssBtnIcon('FieldTable'), 'Browse Templates', testId('intro-templates'),
 | 
			
		||||
      cssButton.cls('-primary'),
 | 
			
		||||
      dom.hide(shouldHideUiElement("templates")),
 | 
			
		||||
      urlState().setLinkUrl({homePage: 'templates'}),
 | 
			
		||||
      )
 | 
			
		||||
    ),
 | 
			
		||||
    !options.import ? null :
 | 
			
		||||
    cssBtn(cssBtnIcon('Import'), 'Import Document', testId('intro-import-doc'),
 | 
			
		||||
      dom.on('click', () => importDocAndOpen(homeModel)),
 | 
			
		||||
    ),
 | 
			
		||||
    !options.empty ? null :
 | 
			
		||||
    cssBtn(cssBtnIcon('Page'), 'Create Empty Document', testId('intro-create-doc'),
 | 
			
		||||
      dom.on('click', () => createDocAndOpen(homeModel)),
 | 
			
		||||
    ),
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function makeCreateButtons(homeModel: HomeModel) {
 | 
			
		||||
  const canManageTeam = homeModel.app.isTeamSite &&
 | 
			
		||||
    roles.canEditAccess(homeModel.app.currentOrg?.access || null);
 | 
			
		||||
  return buildButtons(homeModel, {
 | 
			
		||||
    invite: canManageTeam,
 | 
			
		||||
    templates: !canManageTeam,
 | 
			
		||||
    import: true,
 | 
			
		||||
    empty: true
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const cssParagraph = styled(css.docBlock, `
 | 
			
		||||
  color: ${theme.text};
 | 
			
		||||
  line-height: 1.6;
 | 
			
		||||
 | 
			
		||||
@ -67,6 +67,7 @@ describe('HomeIntro', function() {
 | 
			
		||||
    it('should show examples workspace with the intro', testExamplesSection);
 | 
			
		||||
    it('should allow copying examples', testCopyingExamples.bind(null, undefined));
 | 
			
		||||
    it('should render selected Examples workspace specially', testSelectedExamplesPage);
 | 
			
		||||
    it('should show empty workspace info', testEmptyWorkspace.bind(null, {buttons: true}));
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe("Logged-in on team site", function() {
 | 
			
		||||
@ -91,6 +92,7 @@ describe('HomeIntro', function() {
 | 
			
		||||
    it('should show examples workspace with the intro', testExamplesSection);
 | 
			
		||||
    it('should allow copying examples', testCopyingExamples.bind(null, gu.session().teamSite.orgName));
 | 
			
		||||
    it('should render selected Examples workspace specially', testSelectedExamplesPage);
 | 
			
		||||
    it('should show empty workspace info', testEmptyWorkspace);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  async function testOtherSitesSection() {
 | 
			
		||||
@ -118,13 +120,7 @@ describe('HomeIntro', function() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async function testCreateImport(isLoggedIn: boolean) {
 | 
			
		||||
    // Create doc from intro button
 | 
			
		||||
    await driver.find('.test-intro-create-doc').click();
 | 
			
		||||
    await checkDocAndRestore(isLoggedIn, async () => assert.equal(await gu.getCell('A', 1).getText(), ''));
 | 
			
		||||
 | 
			
		||||
    // Import doc from intro button
 | 
			
		||||
    await gu.fileDialogUpload('uploads/FileUploadData.csv', () => driver.find('.test-intro-import-doc').click());
 | 
			
		||||
    await checkDocAndRestore(isLoggedIn, async () => assert.equal(await gu.getCell('fname', 1).getText(), 'george'));
 | 
			
		||||
    await checkIntroButtons(isLoggedIn);
 | 
			
		||||
 | 
			
		||||
    // Check that add-new menu has enabled Create Empty and Import Doc items.
 | 
			
		||||
    await driver.find('.test-dm-add-new').doClick();
 | 
			
		||||
@ -158,29 +154,7 @@ describe('HomeIntro', function() {
 | 
			
		||||
    assert.isAbove(Number(await img.getAttribute('naturalWidth')), 0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Wait for doc to load, check it, then return to home page, and remove the doc so that we
 | 
			
		||||
  // can see the intro again.
 | 
			
		||||
  const checkDocAndRestore = async function(isLoggedIn: boolean, docChecker: () => Promise<void>,
 | 
			
		||||
                                                          stepsBackToDocMenu: number = 1) {
 | 
			
		||||
    await gu.waitForDocToLoad();
 | 
			
		||||
    await gu.dismissWelcomeTourIfNeeded();
 | 
			
		||||
    await docChecker();
 | 
			
		||||
    for (let i = 0; i < stepsBackToDocMenu; i++) {
 | 
			
		||||
      await driver.navigate().back();
 | 
			
		||||
    }
 | 
			
		||||
    await gu.waitForDocMenuToLoad();
 | 
			
		||||
 | 
			
		||||
    // If not logged in, we create docs "unsaved" and don't see them in doc-menu.
 | 
			
		||||
    if (isLoggedIn) {
 | 
			
		||||
      // Delete the first doc we find. We expect exactly one to exist.
 | 
			
		||||
      assert.equal(await driver.find('.test-dm-doc').isPresent(), true);
 | 
			
		||||
      await driver.find('.test-dm-doc').mouseMove().find('.test-dm-pinned-doc-options').click();
 | 
			
		||||
      await driver.find('.test-dm-delete-doc').click();
 | 
			
		||||
      await driver.find('.test-modal-confirm').click();
 | 
			
		||||
      await driver.wait(async () => !(await driver.find('.test-modal-dialog').isPresent()), 3000);
 | 
			
		||||
    }
 | 
			
		||||
    assert.equal(await driver.find('.test-dm-doc').isPresent(), false);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  async function testExamplesCollapsing() {
 | 
			
		||||
    assert.equal(await driver.find('.test-dm-pinned-doc-name').isDisplayed(), true);
 | 
			
		||||
@ -314,3 +288,77 @@ describe('HomeIntro', function() {
 | 
			
		||||
    await checkImageLoaded(docItem.find('img'));
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function testEmptyWorkspace(options = { buttons: false }) {
 | 
			
		||||
  await gu.openWorkspace("Home");
 | 
			
		||||
  assert.equal(await driver.findWait('.test-empty-workspace-info', 400).isDisplayed(), true);
 | 
			
		||||
  // Create doc and check it is created.
 | 
			
		||||
  await driver.find('.test-intro-create-doc').click();
 | 
			
		||||
  await waitAndDismiss();
 | 
			
		||||
  await emptyDockChecker();
 | 
			
		||||
  // Check that we don't see empty info.
 | 
			
		||||
  await driver.navigate().back();
 | 
			
		||||
  await gu.waitForDocMenuToLoad();
 | 
			
		||||
  assert.equal(await driver.find('.test-empty-workspace-info').isPresent(), false);
 | 
			
		||||
  // Remove created document, it also checks that document is visible.
 | 
			
		||||
  await deleteFirstDoc();
 | 
			
		||||
  assert.equal(await driver.findWait('.test-empty-workspace-info', 400).isDisplayed(), true);
 | 
			
		||||
 | 
			
		||||
  await checkImportDocButton(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Wait for doc to load, check it, then return to home page, and remove the doc so that we
 | 
			
		||||
// can see the intro again.
 | 
			
		||||
async function checkDocAndRestore(
 | 
			
		||||
  isLoggedIn: boolean,
 | 
			
		||||
  docChecker: () => Promise<void>,
 | 
			
		||||
  stepsBackToDocMenu: number = 1)
 | 
			
		||||
{
 | 
			
		||||
  await waitAndDismiss();
 | 
			
		||||
  await docChecker();
 | 
			
		||||
  for (let i = 0; i < stepsBackToDocMenu; i++) {
 | 
			
		||||
    await driver.navigate().back();
 | 
			
		||||
  }
 | 
			
		||||
  await gu.waitForDocMenuToLoad();
 | 
			
		||||
  // If not logged in, we create docs "unsaved" and don't see them in doc-menu.
 | 
			
		||||
  if (isLoggedIn) {
 | 
			
		||||
    // Delete the first doc we find. We expect exactly one to exist.
 | 
			
		||||
    await deleteFirstDoc();
 | 
			
		||||
  }
 | 
			
		||||
  assert.equal(await driver.find('.test-dm-doc').isPresent(), false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function waitAndDismiss() {
 | 
			
		||||
  await gu.waitForDocToLoad();
 | 
			
		||||
  await gu.dismissWelcomeTourIfNeeded();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function deleteFirstDoc() {
 | 
			
		||||
  assert.equal(await driver.find('.test-dm-doc').isPresent(), true);
 | 
			
		||||
  await driver.find('.test-dm-doc').mouseMove().find('.test-dm-pinned-doc-options').click();
 | 
			
		||||
  await driver.find('.test-dm-delete-doc').click();
 | 
			
		||||
  await driver.find('.test-modal-confirm').click();
 | 
			
		||||
  await driver.wait(async () => !(await driver.find('.test-modal-dialog').isPresent()), 3000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function checkIntroButtons(isLoggedIn: boolean) {
 | 
			
		||||
  // Create doc from intro button
 | 
			
		||||
  await checkCreateDocButton(isLoggedIn);
 | 
			
		||||
 | 
			
		||||
  // Import doc from intro button
 | 
			
		||||
  await checkImportDocButton(isLoggedIn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function checkImportDocButton(isLoggedIn: boolean) {
 | 
			
		||||
  await gu.fileDialogUpload('uploads/FileUploadData.csv', () => driver.find('.test-intro-import-doc').click());
 | 
			
		||||
  await checkDocAndRestore(isLoggedIn, async () => assert.equal(await gu.getCell('fname', 1).getText(), 'george'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function checkCreateDocButton(isLoggedIn: boolean) {
 | 
			
		||||
  await driver.find('.test-intro-create-doc').click();
 | 
			
		||||
  await checkDocAndRestore(isLoggedIn, emptyDockChecker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function emptyDockChecker() {
 | 
			
		||||
  assert.equal(await gu.getCell('A', 1).getText(), '');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1471,6 +1471,12 @@ export async function openWsDropdown(wsName: string): Promise<void> {
 | 
			
		||||
  await wsTab.find('.test-dm-workspace-options').mouseMove().click();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function openWorkspace(wsName: string): Promise<void> {
 | 
			
		||||
  const wsTab = await driver.findContentWait('.test-dm-workspace', wsName, 3000);
 | 
			
		||||
  await wsTab.click();
 | 
			
		||||
  await waitForDocMenuToLoad();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Open ⋮ dropdown menu for named document.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user