mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) passing language as a query parameter to custom widgets
Summary: to allow custom widget having optional translations, lagunage seeted in user profile is passed as query parameter to custom widget Test Plan: test added to check if query parameter is existing in url when settings is changed in profile Reviewers: georgegevoian Reviewed By: georgegevoian Subscribers: jarek, paulfitz Differential Revision: https://phab.getgrist.com/D4045
This commit is contained in:
parent
fbae81648c
commit
498ad07d38
@ -25,6 +25,7 @@ import {UserError} from 'app/client/models/errors';
|
||||
import {SortedRowSet} from 'app/client/models/rowset';
|
||||
import {closeRegisteredMenu} from 'app/client/ui2018/menus';
|
||||
import {AccessLevel} from 'app/common/CustomWidget';
|
||||
import {defaultLocale} from 'app/common/gutil';
|
||||
import {PluginInstance} from 'app/common/PluginInstance';
|
||||
import {getGristConfig} from 'app/common/urlUtils';
|
||||
import {Events as BackboneEvents} from 'backbone';
|
||||
@ -213,9 +214,17 @@ export class CustomView extends Disposable {
|
||||
showAfterReady?: boolean,
|
||||
}) {
|
||||
const {baseUrl, access, showAfterReady} = options;
|
||||
const documentSettings = this.gristDoc.docData.docSettings();
|
||||
return grains.create(WidgetFrame, {
|
||||
url: baseUrl || this.getEmptyWidgetPage(),
|
||||
access,
|
||||
preferences:
|
||||
{
|
||||
culture: documentSettings.locale?? defaultLocale,
|
||||
language: this.gristDoc.appModel.currentUser?.locale ?? defaultLocale,
|
||||
timeZone: this.gristDoc.docInfo.timezone() ?? "UTC",
|
||||
currency: documentSettings.currency?? "USD",
|
||||
},
|
||||
readonly: this.gristDoc.isReadonly.get(),
|
||||
showAfterReady,
|
||||
onSettingsInitialized: async () => {
|
||||
|
@ -73,6 +73,10 @@ export interface WidgetFrameOptions {
|
||||
* Optional handler to modify the iframe.
|
||||
*/
|
||||
onElem?: (iframe: HTMLIFrameElement) => void;
|
||||
/**
|
||||
* Optional language to use for the widget.
|
||||
*/
|
||||
preferences: {language?: string, timeZone?: any, currency?: string, culture?: string};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,6 +179,9 @@ export class WidgetFrame extends DisposableWithEvents {
|
||||
const urlObj = new URL(url);
|
||||
urlObj.searchParams.append('access', this._options.access);
|
||||
urlObj.searchParams.append('readonly', String(this._options.readonly));
|
||||
// Append user and document preferences to query string.
|
||||
const settingsParams = new URLSearchParams(this._options.preferences);
|
||||
settingsParams.forEach((value, key) => urlObj.searchParams.append(key, value));
|
||||
return urlObj.href;
|
||||
};
|
||||
const fullUrl = urlWithAccess(this._options.url);
|
||||
|
@ -38,6 +38,11 @@ const widgetFull = fromAccess(AccessLevel.full);
|
||||
// Holds widgets manifest content.
|
||||
let widgets: ICustomWidget[] = [];
|
||||
|
||||
// Helper function to get iframe with custom widget.
|
||||
function getCustomWidgetFrame() {
|
||||
return driver.findWait('iframe', 500);
|
||||
}
|
||||
|
||||
describe('CustomWidgets', function () {
|
||||
this.timeout(20000);
|
||||
const cleanup = setupTestSuite();
|
||||
@ -50,6 +55,8 @@ describe('CustomWidgets', function () {
|
||||
return server.testingHooks.setWidgetRepositoryUrl(url ? `${widgetServerUrl}${url}` : '');
|
||||
}
|
||||
|
||||
|
||||
|
||||
before(async function () {
|
||||
if (server.isExternalServer()) {
|
||||
this.skip();
|
||||
@ -100,8 +107,14 @@ describe('CustomWidgets', function () {
|
||||
// Add custom section.
|
||||
await gu.addNewSection(/Custom/, /Table1/, {selectBy: /TABLE1/});
|
||||
|
||||
// Override gristConfig to enable widget list.
|
||||
await driver.executeScript('window.gristConfig.enableWidgetRepository = true;');
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
await server.testingHooks.setWidgetRepositoryUrl('');
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
await server.testingHooks.setWidgetRepositoryUrl('');
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
@ -109,7 +122,7 @@ describe('CustomWidgets', function () {
|
||||
});
|
||||
|
||||
// Open or close widget menu.
|
||||
const toggle = () => driver.find('.test-config-widget-select .test-select-open').click();
|
||||
const toggle = async () => await driver.findWait('.test-config-widget-select .test-select-open', 1000).click();
|
||||
// Get current value from widget menu.
|
||||
const current = () => driver.find('.test-config-widget-select .test-select-open').getText();
|
||||
// Get options from widget menu (must be first opened).
|
||||
@ -121,19 +134,17 @@ describe('CustomWidgets', function () {
|
||||
};
|
||||
// Get rendered content from custom section.
|
||||
const content = async () => {
|
||||
const iframe = driver.find('iframe');
|
||||
await driver.switchTo().frame(iframe);
|
||||
return gu.doInIframe(await getCustomWidgetFrame(), async ()=>{
|
||||
const text = await driver.find('body').getText();
|
||||
await driver.switchTo().defaultContent();
|
||||
return text;
|
||||
});
|
||||
};
|
||||
|
||||
async function execute(
|
||||
op: (table: TableOperations) => Promise<any>,
|
||||
tableSelector: (grist: any) => TableOperations = (grist) => grist.selectedTable
|
||||
) {
|
||||
const iframe = await driver.find('iframe');
|
||||
await driver.switchTo().frame(iframe);
|
||||
try {
|
||||
return gu.doInIframe(await getCustomWidgetFrame(), async ()=> {
|
||||
const harness = async (done: any) => {
|
||||
const grist = (window as any).grist;
|
||||
grist.ready();
|
||||
@ -157,9 +168,7 @@ describe('CustomWidgets', function () {
|
||||
const result = await driver.executeAsyncScript(cmd);
|
||||
// done callback will return null instead of undefined
|
||||
return result === "__undefined__" ? undefined : result;
|
||||
} finally {
|
||||
await driver.switchTo().defaultContent();
|
||||
}
|
||||
});
|
||||
}
|
||||
// Replace url for the Custom URL widget.
|
||||
const setUrl = async (url: string) => {
|
||||
@ -206,6 +215,17 @@ describe('CustomWidgets', function () {
|
||||
// Rejects new access level.
|
||||
const reject = () => driver.find(".test-config-widget-access-reject").click();
|
||||
|
||||
|
||||
describe('RightWidgetMenu', () => {
|
||||
beforeEach(async function () {
|
||||
// Override gristConfig to enable widget list.
|
||||
await driver.executeScript('window.gristConfig.enableWidgetRepository = true;');
|
||||
// We need to be sure that widget configuration panel is open all the time.
|
||||
await gu.toggleSidePanel('right', 'open');
|
||||
await recreatePanel();
|
||||
await driver.findWait('.test-right-tab-pagewidget', 100).click();
|
||||
});
|
||||
|
||||
it('should show widgets in dropdown', async () => {
|
||||
await gu.toggleSidePanel('right', 'open');
|
||||
await driver.find('.test-right-tab-pagewidget').click();
|
||||
@ -360,6 +380,8 @@ describe('CustomWidgets', function () {
|
||||
});
|
||||
|
||||
it('should switch access level to none on new widget', async () => {
|
||||
widgets = [widget1, widget2];
|
||||
await recreatePanel();
|
||||
await toggle();
|
||||
await select(widget1.name);
|
||||
assert.equal(await access(), AccessLevel.none);
|
||||
@ -533,6 +555,62 @@ describe('CustomWidgets', function () {
|
||||
assert.equal(await content(), AccessLevel.none);
|
||||
});
|
||||
|
||||
it('should offer only custom url when disabled', async () => {
|
||||
await toggle();
|
||||
await select(CUSTOM_URL);
|
||||
await driver.executeScript('window.gristConfig.enableWidgetRepository = false;');
|
||||
await recreatePanel();
|
||||
assert.isTrue(await driver.find('.test-config-widget-url').isDisplayed());
|
||||
assert.isFalse(await driver.find('.test-config-widget-select').isPresent());
|
||||
});
|
||||
});
|
||||
|
||||
describe('gristApiSupport', async ()=>{
|
||||
beforeEach(async function () {
|
||||
// Override gristConfig to enable widget list.
|
||||
await driver.executeScript('window.gristConfig.enableWidgetRepository = true;');
|
||||
// We need to be sure that widget configuration panel is open all the time.
|
||||
await gu.toggleSidePanel('right', 'open');
|
||||
await recreatePanel();
|
||||
await driver.findWait('.test-right-tab-pagewidget', 100).click();
|
||||
});
|
||||
it('should set language in widget url', async () => {
|
||||
function languageMenu() {
|
||||
return gu.currentDriver().find('.test-account-page-language .test-select-open');
|
||||
}
|
||||
async function language() {
|
||||
return await gu.doInIframe(await getCustomWidgetFrame(), async ()=>{
|
||||
const urlText = await driver.executeScript<string>('return document.location.href');
|
||||
const url = new URL(urlText);
|
||||
return url.searchParams.get('language');
|
||||
});
|
||||
}
|
||||
|
||||
async function switchLanguage(lang: string) {
|
||||
await gu.openProfileSettingsPage();
|
||||
await gu.waitForServer();
|
||||
await languageMenu().click();
|
||||
await driver.findContentWait('.test-select-menu li', lang, 100).click();
|
||||
await gu.waitForServer();
|
||||
await driver.navigate().back();
|
||||
await gu.waitForServer();
|
||||
}
|
||||
|
||||
widgets = [widget1];
|
||||
await useManifest(manifestEndpoint);
|
||||
await gu.openWidgetPanel();
|
||||
await toggle();
|
||||
await select(widget1.name);
|
||||
//Switch language to Polish
|
||||
await switchLanguage('Polski');
|
||||
//Check if widgets have "pl" in url
|
||||
assert.equal(await language(), 'pl');
|
||||
//Switch back to English
|
||||
await switchLanguage('English');
|
||||
//Check if widgets have "en" in url
|
||||
assert.equal(await language(), 'en');
|
||||
});
|
||||
|
||||
it("should support grist.selectedTable", async () => {
|
||||
// Open a custom widget with full access.
|
||||
await gu.toggleSidePanel('right', 'open');
|
||||
@ -633,26 +711,14 @@ describe('CustomWidgets', function () {
|
||||
});
|
||||
|
||||
it("should support grist.getAccessTokens", async () => {
|
||||
const iframe = await driver.find('iframe');
|
||||
await driver.switchTo().frame(iframe);
|
||||
try {
|
||||
return await gu.doInIframe(await getCustomWidgetFrame(), async ()=>{
|
||||
const tokenResult: AccessTokenResult = await driver.executeAsyncScript(
|
||||
(done: any) => (window as any).grist.getAccessToken().then(done)
|
||||
);
|
||||
assert.sameMembers(Object.keys(tokenResult), ['ttlMsecs', 'token', 'baseUrl']);
|
||||
const result = await fetch(tokenResult.baseUrl + `/tables/Table1/records?auth=${tokenResult.token}`);
|
||||
assert.sameMembers(Object.keys(await result.json()), ['records']);
|
||||
} finally {
|
||||
await driver.switchTo().defaultContent();
|
||||
}
|
||||
});
|
||||
|
||||
it('should offer only custom url when disabled', async () => {
|
||||
await toggle();
|
||||
await select(CUSTOM_URL);
|
||||
await driver.executeScript('window.gristConfig.enableWidgetRepository = false;');
|
||||
await recreatePanel();
|
||||
assert.isTrue(await driver.find('.test-config-widget-url').isDisplayed());
|
||||
assert.isFalse(await driver.find('.test-config-widget-select').isPresent());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -859,6 +859,19 @@ export async function importUrlDialog(url: string): Promise<void> {
|
||||
await driver.switchTo().defaultContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed passed function in the context of given iframe, and then switching back to original context
|
||||
*
|
||||
*/
|
||||
export async function doInIframe<T>(iframe: WebElement, func: () => Promise<T>) {
|
||||
try {
|
||||
await driver.switchTo().frame(iframe);
|
||||
return await func();
|
||||
} finally {
|
||||
await driver.switchTo().defaultContent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts or resets the collections of UserActions. This should be followed some time later by
|
||||
* a call to userActionsVerify() to check which UserActions were sent to the server. If the
|
||||
|
Loading…
Reference in New Issue
Block a user