mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +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 {getTimeFromNow, HomeModel, makeLocalViewSettings, ViewSettings} from 'app/client/models/HomeModel';
|
||||||
import {getWorkspaceInfo, workspaceName} from 'app/client/models/WorkspaceInfo';
|
import {getWorkspaceInfo, workspaceName} from 'app/client/models/WorkspaceInfo';
|
||||||
import * as css from 'app/client/ui/DocMenuCss';
|
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 {buildUpgradeButton} from 'app/client/ui/ProductUpgrades';
|
||||||
import {buildPinnedDoc, createPinnedDocs} from 'app/client/ui/PinnedDocs';
|
import {buildPinnedDoc, createPinnedDocs} from 'app/client/ui/PinnedDocs';
|
||||||
import {shadowScroll} from 'app/client/ui/shadowScroll';
|
import {shadowScroll} from 'app/client/ui/shadowScroll';
|
||||||
@ -134,12 +134,14 @@ function createLoadedDocMenu(owner: IDisposableOwner, home: HomeModel) {
|
|||||||
dom('div',
|
dom('div',
|
||||||
buildAllTemplates(home, home.templateWorkspaces, viewSettings)
|
buildAllTemplates(home, home.templateWorkspaces, viewSettings)
|
||||||
) :
|
) :
|
||||||
workspace && !workspace.isSupportWorkspace ?
|
workspace && !workspace.isSupportWorkspace && workspace.docs?.length ?
|
||||||
css.docBlock(
|
css.docBlock(
|
||||||
buildWorkspaceDocBlock(home, workspace, flashDocId, viewSettings),
|
buildWorkspaceDocBlock(home, workspace, flashDocId, viewSettings),
|
||||||
testId('doc-block')
|
testId('doc-block')
|
||||||
) :
|
) :
|
||||||
css.docBlock('Workspace not found')
|
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) {
|
function makeViewerTeamSiteIntro(homeModel: HomeModel) {
|
||||||
const personalOrg = Computed.create(null, use => use(homeModel.app.topAppModel.orgs).find(o => o.owner));
|
const personalOrg = Computed.create(null, use => use(homeModel.app.topAppModel.orgs).find(o => o.owner));
|
||||||
const docLink = (dom.maybe(personalOrg, org => {
|
const docLink = (dom.maybe(personalOrg, org => {
|
||||||
@ -105,31 +124,46 @@ function helpCenterLink() {
|
|||||||
return cssLink({href: commonUrls.help, target: '_blank'}, cssInlineIcon('Help'), 'Help Center');
|
return cssLink({href: commonUrls.help, target: '_blank'}, cssInlineIcon('Help'), 'Help Center');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildButtons(homeModel: HomeModel, options: {
|
||||||
function makeCreateButtons(homeModel: HomeModel) {
|
invite: boolean,
|
||||||
const canManageTeam = homeModel.app.isTeamSite &&
|
templates: boolean,
|
||||||
roles.canEditAccess(homeModel.app.currentOrg?.access || null);
|
import: boolean,
|
||||||
|
empty: boolean,
|
||||||
|
}) {
|
||||||
return cssBtnGroup(
|
return cssBtnGroup(
|
||||||
(canManageTeam ?
|
!options.invite ? null :
|
||||||
cssBtn(cssBtnIcon('Help'), 'Invite Team Members', testId('intro-invite'),
|
cssBtn(cssBtnIcon('Help'), 'Invite Team Members', testId('intro-invite'),
|
||||||
cssButton.cls('-primary'),
|
cssButton.cls('-primary'),
|
||||||
dom.on('click', () => manageTeamUsersApp(homeModel.app)),
|
dom.on('click', () => manageTeamUsersApp(homeModel.app)),
|
||||||
) :
|
|
||||||
cssBtn(cssBtnIcon('FieldTable'), 'Browse Templates', testId('intro-templates'),
|
|
||||||
cssButton.cls('-primary'),
|
|
||||||
dom.hide(shouldHideUiElement("templates")),
|
|
||||||
urlState().setLinkUrl({homePage: 'templates'}),
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
!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'),
|
cssBtn(cssBtnIcon('Import'), 'Import Document', testId('intro-import-doc'),
|
||||||
dom.on('click', () => importDocAndOpen(homeModel)),
|
dom.on('click', () => importDocAndOpen(homeModel)),
|
||||||
),
|
),
|
||||||
|
!options.empty ? null :
|
||||||
cssBtn(cssBtnIcon('Page'), 'Create Empty Document', testId('intro-create-doc'),
|
cssBtn(cssBtnIcon('Page'), 'Create Empty Document', testId('intro-create-doc'),
|
||||||
dom.on('click', () => createDocAndOpen(homeModel)),
|
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, `
|
const cssParagraph = styled(css.docBlock, `
|
||||||
color: ${theme.text};
|
color: ${theme.text};
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
|
@ -67,6 +67,7 @@ describe('HomeIntro', function() {
|
|||||||
it('should show examples workspace with the intro', testExamplesSection);
|
it('should show examples workspace with the intro', testExamplesSection);
|
||||||
it('should allow copying examples', testCopyingExamples.bind(null, undefined));
|
it('should allow copying examples', testCopyingExamples.bind(null, undefined));
|
||||||
it('should render selected Examples workspace specially', testSelectedExamplesPage);
|
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() {
|
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 show examples workspace with the intro', testExamplesSection);
|
||||||
it('should allow copying examples', testCopyingExamples.bind(null, gu.session().teamSite.orgName));
|
it('should allow copying examples', testCopyingExamples.bind(null, gu.session().teamSite.orgName));
|
||||||
it('should render selected Examples workspace specially', testSelectedExamplesPage);
|
it('should render selected Examples workspace specially', testSelectedExamplesPage);
|
||||||
|
it('should show empty workspace info', testEmptyWorkspace);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function testOtherSitesSection() {
|
async function testOtherSitesSection() {
|
||||||
@ -118,13 +120,7 @@ describe('HomeIntro', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function testCreateImport(isLoggedIn: boolean) {
|
async function testCreateImport(isLoggedIn: boolean) {
|
||||||
// Create doc from intro button
|
await checkIntroButtons(isLoggedIn);
|
||||||
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'));
|
|
||||||
|
|
||||||
// Check that add-new menu has enabled Create Empty and Import Doc items.
|
// Check that add-new menu has enabled Create Empty and Import Doc items.
|
||||||
await driver.find('.test-dm-add-new').doClick();
|
await driver.find('.test-dm-add-new').doClick();
|
||||||
@ -158,29 +154,7 @@ describe('HomeIntro', function() {
|
|||||||
assert.isAbove(Number(await img.getAttribute('naturalWidth')), 0);
|
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() {
|
async function testExamplesCollapsing() {
|
||||||
assert.equal(await driver.find('.test-dm-pinned-doc-name').isDisplayed(), true);
|
assert.equal(await driver.find('.test-dm-pinned-doc-name').isDisplayed(), true);
|
||||||
@ -314,3 +288,77 @@ describe('HomeIntro', function() {
|
|||||||
await checkImageLoaded(docItem.find('img'));
|
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();
|
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.
|
* Open ⋮ dropdown menu for named document.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user