mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Add tip for calendar widget configuration
Summary: The tip is shown in the creator panel, in the subtab that lists the column mapping configuration for the calendar widget. The panel now automatically opens the first time a calendar widget is added to a page (via the Add New menu). Test Plan: Manual. Reviewers: JakubSerafin Reviewed By: JakubSerafin Subscribers: JakubSerafin, jarek Differential Revision: https://phab.getgrist.com/D4047
This commit is contained in:
parent
581a62306e
commit
927e92e3e8
@ -21,6 +21,7 @@ export interface AttachOptions {
|
|||||||
showOnMobile?: boolean;
|
showOnMobile?: boolean;
|
||||||
popupOptions?: IPopupOptions;
|
popupOptions?: IPopupOptions;
|
||||||
onDispose?(): void;
|
onDispose?(): void;
|
||||||
|
shouldShow?(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QueuedTip {
|
interface QueuedTip {
|
||||||
@ -146,6 +147,7 @@ export class BehavioralPromptsManager extends Disposable {
|
|||||||
private _shouldQueueTip(prompt: BehavioralPrompt, options: AttachOptions) {
|
private _shouldQueueTip(prompt: BehavioralPrompt, options: AttachOptions) {
|
||||||
if (
|
if (
|
||||||
this._isDisabled ||
|
this._isDisabled ||
|
||||||
|
options.shouldShow?.() === false ||
|
||||||
(isNarrowScreen() && !options.showOnMobile) ||
|
(isNarrowScreen() && !options.showOnMobile) ||
|
||||||
(this._prefs.get().dontShowTips && !options.forceShow) ||
|
(this._prefs.get().dontShowTips && !options.forceShow) ||
|
||||||
this.hasSeenTip(prompt)
|
this.hasSeenTip(prompt)
|
||||||
|
@ -47,7 +47,7 @@ import {IPageWidget, toPageWidget} from 'app/client/ui/PageWidgetPicker';
|
|||||||
import {linkFromId, selectBy} from 'app/client/ui/selectBy';
|
import {linkFromId, selectBy} from 'app/client/ui/selectBy';
|
||||||
import {WebhookPage} from 'app/client/ui/WebhookPage';
|
import {WebhookPage} from 'app/client/ui/WebhookPage';
|
||||||
import {startWelcomeTour} from 'app/client/ui/WelcomeTour';
|
import {startWelcomeTour} from 'app/client/ui/WelcomeTour';
|
||||||
import {IWidgetType} from 'app/common/widgetTypes';
|
import {AttachedCustomWidgets, IAttachedCustomWidget, IWidgetType} from 'app/common/widgetTypes';
|
||||||
import {PlayerState, YouTubePlayer} from 'app/client/ui/YouTubePlayer';
|
import {PlayerState, YouTubePlayer} from 'app/client/ui/YouTubePlayer';
|
||||||
import {isNarrowScreen, mediaSmall, mediaXSmall, testId, theme} from 'app/client/ui2018/cssVars';
|
import {isNarrowScreen, mediaSmall, mediaXSmall, testId, theme} from 'app/client/ui2018/cssVars';
|
||||||
import {IconName} from 'app/client/ui2018/IconList';
|
import {IconName} from 'app/client/ui2018/IconList';
|
||||||
@ -837,6 +837,10 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
this.viewModel.activeSectionId(res.sectionRef);
|
this.viewModel.activeSectionId(res.sectionRef);
|
||||||
|
|
||||||
this._maybeShowEditCardLayoutTip(val.type).catch(reportError);
|
this._maybeShowEditCardLayoutTip(val.type).catch(reportError);
|
||||||
|
|
||||||
|
if (AttachedCustomWidgets.guard(val.type)) {
|
||||||
|
this._handleNewAttachedCustomWidget(val.type).catch(reportError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -881,6 +885,10 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
this.viewModel.activeSectionId(result.sectionRef);
|
this.viewModel.activeSectionId(result.sectionRef);
|
||||||
|
|
||||||
this._maybeShowEditCardLayoutTip(val.type).catch(reportError);
|
this._maybeShowEditCardLayoutTip(val.type).catch(reportError);
|
||||||
|
|
||||||
|
if (AttachedCustomWidgets.guard(val.type)) {
|
||||||
|
this._handleNewAttachedCustomWidget(val.type).catch(reportError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1438,6 +1446,19 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _handleNewAttachedCustomWidget(widget: IAttachedCustomWidget) {
|
||||||
|
switch (widget) {
|
||||||
|
case 'custom.calendar': {
|
||||||
|
// Open the right panel to the calendar subtab.
|
||||||
|
commands.allCommands.viewTabOpen.run();
|
||||||
|
|
||||||
|
// Wait for the right panel to finish animation if it was collapsed before.
|
||||||
|
await commands.allCommands.rightPanelOpen.run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _promptForName() {
|
private async _promptForName() {
|
||||||
return await invokePrompt("Table name", "Create", '', "Default table name");
|
return await invokePrompt("Table name", "Create", '', "Default table name");
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ class ColumnListPicker extends Disposable {
|
|||||||
class CustomSectionConfigurationConfig extends Disposable{
|
class CustomSectionConfigurationConfig extends Disposable{
|
||||||
// Does widget has custom configuration.
|
// Does widget has custom configuration.
|
||||||
private readonly _hasConfiguration: Computed<boolean>;
|
private readonly _hasConfiguration: Computed<boolean>;
|
||||||
constructor(private _section: ViewSectionRec) {
|
constructor(private _section: ViewSectionRec, private _gristDoc: GristDoc) {
|
||||||
super();
|
super();
|
||||||
this._hasConfiguration = Computed.create(this, use => use(_section.hasCustomOptions));
|
this._hasConfiguration = Computed.create(this, use => use(_section.hasCustomOptions));
|
||||||
}
|
}
|
||||||
@ -268,11 +268,12 @@ class CustomSectionConfigurationConfig extends Disposable{
|
|||||||
value: createObs(column),
|
value: createObs(column),
|
||||||
column
|
column
|
||||||
}));
|
}));
|
||||||
return [
|
return dom('div',
|
||||||
|
this._attachColumnMappingTip(this._section.customDef.url()),
|
||||||
...mappings.map(m => m.column.allowMultiple
|
...mappings.map(m => m.column.allowMultiple
|
||||||
? dom.create(ColumnListPicker, m.value, m.column, this._section)
|
? dom.create(ColumnListPicker, m.value, m.column, this._section)
|
||||||
: dom.create(ColumnPicker, m.value, m.column, this._section))
|
: dom.create(ColumnPicker, m.value, m.column, this._section)),
|
||||||
];
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -280,7 +281,19 @@ class CustomSectionConfigurationConfig extends Disposable{
|
|||||||
allCommands.openWidgetConfiguration.run();
|
allCommands.openWidgetConfiguration.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _attachColumnMappingTip(widgetUrl: string | null) {
|
||||||
|
switch (widgetUrl) {
|
||||||
|
// TODO: come up with a way to attach tips without hardcoding widget URLs.
|
||||||
|
case 'https://gristlabs.github.io/grist-widget/calendar/index.html': {
|
||||||
|
return this._gristDoc.behavioralPromptsManager.attachTip('calendarConfig', {
|
||||||
|
popupOptions: {placement: 'left-start'},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CustomSectionConfig extends Disposable {
|
export class CustomSectionConfig extends Disposable {
|
||||||
@ -305,7 +318,7 @@ export class CustomSectionConfig extends Disposable {
|
|||||||
|
|
||||||
constructor(protected _section: ViewSectionRec, private _gristDoc: GristDoc) {
|
constructor(protected _section: ViewSectionRec, private _gristDoc: GristDoc) {
|
||||||
super();
|
super();
|
||||||
this._customSectionConfigurationConfig = new CustomSectionConfigurationConfig(_section);
|
this._customSectionConfigurationConfig = new CustomSectionConfigurationConfig(_section, _gristDoc);
|
||||||
|
|
||||||
// Test if we can offer widget list.
|
// Test if we can offer widget list.
|
||||||
const gristConfig: GristLoadConfig = (window as any).gristConfig || {};
|
const gristConfig: GristLoadConfig = (window as any).gristConfig || {};
|
||||||
@ -467,7 +480,11 @@ export class CustomSectionConfig extends Disposable {
|
|||||||
this._gristDoc.behavioralPromptsManager.attachTip('customURL', {
|
this._gristDoc.behavioralPromptsManager.attachTip('customURL', {
|
||||||
popupOptions: {
|
popupOptions: {
|
||||||
placement: 'left-start',
|
placement: 'left-start',
|
||||||
}
|
},
|
||||||
|
shouldShow: () => {
|
||||||
|
// Only show tip if a custom widget isn't already selected.
|
||||||
|
return !this._selectedId.get() || (isCustom.get() && this._url.get().trim() === '');
|
||||||
|
},
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
@ -240,4 +240,16 @@ to determine who can see or edit which parts of your document.')),
|
|||||||
),
|
),
|
||||||
deploymentTypes: ['saas'],
|
deploymentTypes: ['saas'],
|
||||||
},
|
},
|
||||||
|
calendarConfig: {
|
||||||
|
title: () => t('Calendar'),
|
||||||
|
content: (...args: DomElementArg[]) => cssTooltipContent(
|
||||||
|
dom('div', t("To configure your calendar, select columns for start/end dates and event titles. \
|
||||||
|
Note each column's type.")),
|
||||||
|
dom('div', t("Can't find the right columns? Click 'Change Widget' to select the table with events \
|
||||||
|
data.")),
|
||||||
|
dom('div', cssLink({href: commonUrls.helpCalendarWidget, target: '_blank'}, t('Learn more.'))),
|
||||||
|
...args,
|
||||||
|
),
|
||||||
|
deploymentTypes: ['saas'],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -87,6 +87,7 @@ export const BehavioralPrompt = StringUnion(
|
|||||||
'addNew',
|
'addNew',
|
||||||
'rickRow',
|
'rickRow',
|
||||||
'customURL',
|
'customURL',
|
||||||
|
'calendarConfig',
|
||||||
);
|
);
|
||||||
export type BehavioralPrompt = typeof BehavioralPrompt.type;
|
export type BehavioralPrompt = typeof BehavioralPrompt.type;
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@ export const commonUrls = {
|
|||||||
helpTryingOutChanges: "https://support.getgrist.com/copying-docs/#trying-out-changes",
|
helpTryingOutChanges: "https://support.getgrist.com/copying-docs/#trying-out-changes",
|
||||||
helpCustomWidgets: "https://support.getgrist.com/widget-custom",
|
helpCustomWidgets: "https://support.getgrist.com/widget-custom",
|
||||||
helpTelemetryLimited: "https://support.getgrist.com/telemetry-limited",
|
helpTelemetryLimited: "https://support.getgrist.com/telemetry-limited",
|
||||||
|
helpCalendarWidget: "https://support.getgrist.com/widget-calendar",
|
||||||
plans: "https://www.getgrist.com/pricing",
|
plans: "https://www.getgrist.com/pricing",
|
||||||
sproutsProgram: "https://www.getgrist.com/sprouts-program",
|
sproutsProgram: "https://www.getgrist.com/sprouts-program",
|
||||||
contact: "https://www.getgrist.com/contact",
|
contact: "https://www.getgrist.com/contact",
|
||||||
|
@ -7,7 +7,7 @@ import {server, setupTestSuite} from "test/nbrowser/testUtils";
|
|||||||
import {serveSomething} from "test/server/customUtil";
|
import {serveSomething} from "test/server/customUtil";
|
||||||
import {EnvironmentSnapshot} from "test/server/testUtils";
|
import {EnvironmentSnapshot} from "test/server/testUtils";
|
||||||
|
|
||||||
describe('attachedCustomWidget NotepadWidget', function () {
|
describe('AttachedCustomWidget', function () {
|
||||||
this.timeout(20000);
|
this.timeout(20000);
|
||||||
const cleanup = setupTestSuite();
|
const cleanup = setupTestSuite();
|
||||||
let oldEnv: EnvironmentSnapshot;
|
let oldEnv: EnvironmentSnapshot;
|
||||||
@ -84,7 +84,7 @@ describe('attachedCustomWidget NotepadWidget', function () {
|
|||||||
it('should not ask for permission', async () => {
|
it('should not ask for permission', async () => {
|
||||||
await gu.addNewSection(/Calendar/, /Table1/, {selectBy: /TABLE1/});
|
await gu.addNewSection(/Calendar/, /Table1/, {selectBy: /TABLE1/});
|
||||||
await gu.getSection('TABLE1 Calendar').click();
|
await gu.getSection('TABLE1 Calendar').click();
|
||||||
await gu.toggleSidePanel('right', 'open');
|
await gu.waitForSidePanel();
|
||||||
await driver.find('.test-right-tab-pagewidget').click();
|
await driver.find('.test-right-tab-pagewidget').click();
|
||||||
|
|
||||||
await gu.waitForServer();
|
await gu.waitForServer();
|
Loading…
Reference in New Issue
Block a user