mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(core) updates from grist-core
This commit is contained in:
		
						commit
						fb276bade7
					
				@ -9,8 +9,11 @@ import {icon} from 'app/client/ui2018/icons';
 | 
			
		||||
import {cssModalTooltip, modalTooltip} from 'app/client/ui2018/modals';
 | 
			
		||||
import {dom, DomContents, keyframes, observable, styled, svg} from 'grainjs';
 | 
			
		||||
import {IPopupOptions} from 'popweasel';
 | 
			
		||||
import {makeT} from 'app/client/lib/localization';
 | 
			
		||||
import merge = require('lodash/merge');
 | 
			
		||||
 | 
			
		||||
const t = makeT('modals');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is a file for all custom and pre-configured popups, modals, toasts and tooltips, used
 | 
			
		||||
 * in more then one component.
 | 
			
		||||
@ -35,19 +38,21 @@ export function buildConfirmDelete(
 | 
			
		||||
        Escape: () => ctl.close(),
 | 
			
		||||
        Enter: () => { onSave(remember.get()); ctl.close(); },
 | 
			
		||||
      }),
 | 
			
		||||
      dom('div', `Are you sure you want to delete ${single ? 'this' : 'these'} record${single ? '' : 's'}?`,
 | 
			
		||||
      dom('div', single ?
 | 
			
		||||
        t(`Are you sure you want to delete this record?`)
 | 
			
		||||
        : t(`Are you sure you want to delete these records?`),
 | 
			
		||||
        dom.style('margin-bottom', '10px'),
 | 
			
		||||
      ),
 | 
			
		||||
      dom('div',
 | 
			
		||||
        labeledSquareCheckbox(remember, "Don't ask again.", testId('confirm-remember')),
 | 
			
		||||
        labeledSquareCheckbox(remember, t("Don't ask again."), testId('confirm-remember')),
 | 
			
		||||
        dom.style('margin-bottom', '10px'),
 | 
			
		||||
      ),
 | 
			
		||||
      cssButtons(
 | 
			
		||||
        primaryButton('Delete', testId('confirm-save'), dom.on('click', () => {
 | 
			
		||||
        primaryButton(t('Delete'), testId('confirm-save'), dom.on('click', () => {
 | 
			
		||||
          onSave(remember.get());
 | 
			
		||||
          ctl.close();
 | 
			
		||||
        })),
 | 
			
		||||
        basicButton('Cancel', testId('confirm-cancel'), dom.on('click', () => ctl.close()))
 | 
			
		||||
        basicButton(t('Cancel'), testId('confirm-cancel'), dom.on('click', () => ctl.close()))
 | 
			
		||||
      )
 | 
			
		||||
    ), {}
 | 
			
		||||
  );
 | 
			
		||||
@ -81,9 +86,9 @@ export function showDeprecatedWarning(
 | 
			
		||||
        dom.style('justify-content', 'space-between'),
 | 
			
		||||
        dom.style('align-items', 'center'),
 | 
			
		||||
        dom('div',
 | 
			
		||||
          labeledSquareCheckbox(remember, "Don't show again.", testId('confirm-remember')),
 | 
			
		||||
          labeledSquareCheckbox(remember, t("Don't show again."), testId('confirm-remember')),
 | 
			
		||||
        ),
 | 
			
		||||
        basicButton('Dismiss', testId('confirm-save'),
 | 
			
		||||
        basicButton(t('Dismiss'), testId('confirm-save'),
 | 
			
		||||
          dom.on('click', () => { ctl.close(); onClose(remember.get()); })
 | 
			
		||||
        )
 | 
			
		||||
      ),
 | 
			
		||||
@ -105,7 +110,7 @@ export function showDeprecatedWarning(
 | 
			
		||||
export function reportUndo(
 | 
			
		||||
  doc: GristDoc,
 | 
			
		||||
  messageLabel: string,
 | 
			
		||||
  buttonLabel = 'Undo to restore'
 | 
			
		||||
  buttonLabel = t('Undo to restore')
 | 
			
		||||
) {
 | 
			
		||||
  // First create a notification with a button to undo the delete.
 | 
			
		||||
  let notification = reportSuccess(messageLabel, {
 | 
			
		||||
@ -179,12 +184,12 @@ export function showBehavioralPrompt(
 | 
			
		||||
            dom.style('align-items', 'center'),
 | 
			
		||||
            dom('div',
 | 
			
		||||
              cssSkipTipsCheckbox(dontShowTips,
 | 
			
		||||
                cssSkipTipsCheckboxLabel("Don't show tips"),
 | 
			
		||||
                cssSkipTipsCheckboxLabel(t("Don't show tips")),
 | 
			
		||||
                testId('behavioral-prompt-dont-show-tips')
 | 
			
		||||
              ),
 | 
			
		||||
              dom.style('visibility', hideDontShowTips ? 'hidden' : ''),
 | 
			
		||||
            ),
 | 
			
		||||
            cssDismissPromptButton('Got it', testId('behavioral-prompt-dismiss'),
 | 
			
		||||
            cssDismissPromptButton(t('Got it'), testId('behavioral-prompt-dismiss'),
 | 
			
		||||
              dom.on('click', () => { onClose(dontShowTips.get()); ctl.close(); })
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
@ -526,11 +526,19 @@ export class Client {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async _onMessage(message: string): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
      await this._onMessageImpl(message);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      this._log.warn(null, 'onMessage error received for message "%s": %s', shortDesc(message), err.stack);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Processes a request from a client. All requests from a client get a response, at least to
 | 
			
		||||
   * indicate success or failure.
 | 
			
		||||
   */
 | 
			
		||||
  private async _onMessage(message: string): Promise<void> {
 | 
			
		||||
  private async _onMessageImpl(message: string): Promise<void> {
 | 
			
		||||
    const request = JSON.parse(message);
 | 
			
		||||
    if (request.beat) {
 | 
			
		||||
      // this is a heart beat, to keep the websocket alive.  No need to reply.
 | 
			
		||||
 | 
			
		||||
@ -433,7 +433,19 @@
 | 
			
		||||
        "Created by": "Created by",
 | 
			
		||||
        "Detect duplicates in...": "Detect duplicates in...",
 | 
			
		||||
        "Last updated at": "Last updated at",
 | 
			
		||||
        "Last updated by": "Last updated by"
 | 
			
		||||
        "Last updated by": "Last updated by",
 | 
			
		||||
        "Any": "Any",
 | 
			
		||||
        "Numeric": "Numeric",
 | 
			
		||||
        "Text": "Text",
 | 
			
		||||
        "Integer": "Integer",
 | 
			
		||||
        "Toggle": "Toggle",
 | 
			
		||||
        "Date": "Date",
 | 
			
		||||
        "DateTime": "DateTime",
 | 
			
		||||
        "Choice": "Choice",
 | 
			
		||||
        "Choice List": "Choice List",
 | 
			
		||||
        "Reference": "Reference",
 | 
			
		||||
        "Reference List": "Reference List",
 | 
			
		||||
        "Attachment": "Attachment"
 | 
			
		||||
    },
 | 
			
		||||
    "GristDoc": {
 | 
			
		||||
        "Added new linked section to view {{viewName}}": "Added new linked section to view {{viewName}}",
 | 
			
		||||
@ -622,7 +634,23 @@
 | 
			
		||||
        "Widget": "Widget",
 | 
			
		||||
        "You do not have edit access to this document": "You do not have edit access to this document",
 | 
			
		||||
        "Add referenced columns": "Add referenced columns",
 | 
			
		||||
        "Reset form": "Reset form"
 | 
			
		||||
        "Reset form": "Reset form",
 | 
			
		||||
        "Configuration": "Configuration",
 | 
			
		||||
        "Default field value": "Default field value",
 | 
			
		||||
        "Display button": "Display button",
 | 
			
		||||
        "Enter text": "Enter text",
 | 
			
		||||
        "Field rules": "Field rules",
 | 
			
		||||
        "Field title": "Field title",
 | 
			
		||||
        "Hidden field": "Hidden field",
 | 
			
		||||
        "Layout": "Layout",
 | 
			
		||||
        "Redirect automatically after submission": "Redirect automatically after submission",
 | 
			
		||||
        "Redirection": "Redirection",
 | 
			
		||||
        "Required field": "Required field",
 | 
			
		||||
        "Submission": "Submission",
 | 
			
		||||
        "Submit another response": "Submit another response",
 | 
			
		||||
        "Submit button label": "Submit button label",
 | 
			
		||||
        "Success text": "Success text",
 | 
			
		||||
        "Table column name": "Table column name"
 | 
			
		||||
    },
 | 
			
		||||
    "RowContextMenu": {
 | 
			
		||||
        "Copy anchor link": "Copy anchor link",
 | 
			
		||||
@ -761,7 +789,8 @@
 | 
			
		||||
        "Show raw data": "Show raw data",
 | 
			
		||||
        "Widget options": "Widget options",
 | 
			
		||||
        "Add to page": "Add to page",
 | 
			
		||||
        "Collapse widget": "Collapse widget"
 | 
			
		||||
        "Collapse widget": "Collapse widget",
 | 
			
		||||
        "Create a form": "Create a form"
 | 
			
		||||
    },
 | 
			
		||||
    "ViewSectionMenu": {
 | 
			
		||||
        "(customized)": "(customized)",
 | 
			
		||||
@ -863,7 +892,16 @@
 | 
			
		||||
    "modals": {
 | 
			
		||||
        "Cancel": "Cancel",
 | 
			
		||||
        "Ok": "OK",
 | 
			
		||||
        "Save": "Save"
 | 
			
		||||
        "Save": "Save",
 | 
			
		||||
        "Are you sure you want to delete these records?": "Are you sure you want to delete these records?",
 | 
			
		||||
        "Are you sure you want to delete this record?": "Are you sure you want to delete this record?",
 | 
			
		||||
        "Delete": "Delete",
 | 
			
		||||
        "Dismiss": "Dismiss",
 | 
			
		||||
        "Don't ask again.": "Don't ask again.",
 | 
			
		||||
        "Don't show again.": "Don't show again.",
 | 
			
		||||
        "Don't show tips": "Don't show tips",
 | 
			
		||||
        "Undo to restore": "Undo to restore",
 | 
			
		||||
        "Got it": "Got it"
 | 
			
		||||
    },
 | 
			
		||||
    "pages": {
 | 
			
		||||
        "Duplicate Page": "Duplicate Page",
 | 
			
		||||
@ -1266,5 +1304,28 @@
 | 
			
		||||
        "Publish your form?": "Publish your form?",
 | 
			
		||||
        "Unpublish": "Unpublish",
 | 
			
		||||
        "Unpublish your form?": "Unpublish your form?"
 | 
			
		||||
    },
 | 
			
		||||
    "Editor": {
 | 
			
		||||
        "Delete": "Delete"
 | 
			
		||||
    },
 | 
			
		||||
    "Menu": {
 | 
			
		||||
        "Building blocks": "Building blocks",
 | 
			
		||||
        "Columns": "Columns",
 | 
			
		||||
        "Copy": "Copy",
 | 
			
		||||
        "Cut": "Cut",
 | 
			
		||||
        "Insert question above": "Insert question above",
 | 
			
		||||
        "Insert question below": "Insert question below",
 | 
			
		||||
        "Paragraph": "Paragraph",
 | 
			
		||||
        "Paste": "Paste",
 | 
			
		||||
        "Separator": "Separator",
 | 
			
		||||
        "Unmapped fields": "Unmapped fields"
 | 
			
		||||
    },
 | 
			
		||||
    "UnmappedFieldsConfig": {
 | 
			
		||||
        "Clear": "Clear",
 | 
			
		||||
        "Map fields": "Map fields",
 | 
			
		||||
        "Mapped": "Mapped",
 | 
			
		||||
        "Select All": "Select All",
 | 
			
		||||
        "Unmap fields": "Unmap fields",
 | 
			
		||||
        "Unmapped": "Unmapped"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -188,6 +188,24 @@ describe('Comm', function() {
 | 
			
		||||
      ]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should only log warning for malformed JSON data', async function () {
 | 
			
		||||
      const logMessages  = await testUtils.captureLog('warn', async () => {
 | 
			
		||||
        ws.send('foobar');
 | 
			
		||||
      }, {waitForFirstLog: true});
 | 
			
		||||
      testUtils.assertMatchArray(logMessages, [
 | 
			
		||||
        /^warn: Client.* Unexpected token.*/
 | 
			
		||||
      ]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should log warning when null value is passed', async function () {
 | 
			
		||||
      const logMessages  = await testUtils.captureLog('warn', async () => {
 | 
			
		||||
        ws.send('null');
 | 
			
		||||
      }, {waitForFirstLog: true});
 | 
			
		||||
      testUtils.assertMatchArray(logMessages, [
 | 
			
		||||
        /^warn: Client.*Cannot read properties of null*/
 | 
			
		||||
      ]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("should support app-level events correctly", async function() {
 | 
			
		||||
      comm!.broadcastMessage('fooType' as any, 'hello');
 | 
			
		||||
      comm!.broadcastMessage('barType' as any, 'world');
 | 
			
		||||
 | 
			
		||||
@ -126,25 +126,32 @@ export function setTmpLogLevel(level: string, optCaptureFunc?: (level: string, m
 | 
			
		||||
 */
 | 
			
		||||
export async function captureLog(
 | 
			
		||||
  minLevel: string, callback: (messages: string[]) => void|Promise<void>,
 | 
			
		||||
  options: {timestamp: boolean} = {timestamp: false}
 | 
			
		||||
  options: {timestamp?: boolean, waitForFirstLog?: boolean} = {timestamp: false, waitForFirstLog: false}
 | 
			
		||||
): Promise<string[]> {
 | 
			
		||||
  const messages: string[] = [];
 | 
			
		||||
  const prevLogLevel = log.transports.file.level;
 | 
			
		||||
  const name = _.uniqueId('CaptureLog');
 | 
			
		||||
 | 
			
		||||
  function capture(level: string, msg: string, meta: any) {
 | 
			
		||||
    if ((log as any).levels[level] <= (log as any).levels[minLevel]) {  // winston types are off?
 | 
			
		||||
      const timePrefix = options.timestamp ? new Date().toISOString() + ' ' : '';
 | 
			
		||||
      messages.push(`${timePrefix}${level}: ${msg}${meta ? ' ' + serialize(meta) : ''}`);
 | 
			
		||||
  const captureFirstLogPromise = new Promise((resolve) => {
 | 
			
		||||
    function capture(level: string, msg: string, meta: any) {
 | 
			
		||||
      if ((log as any).levels[level] <= (log as any).levels[minLevel]) {  // winston types are off?
 | 
			
		||||
        const timePrefix = options.timestamp ? new Date().toISOString() + ' ' : '';
 | 
			
		||||
        messages.push(`${timePrefix}${level}: ${msg}${meta ? ' ' + serialize(meta) : ''}`);
 | 
			
		||||
        resolve(null);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!process.env.VERBOSE) {
 | 
			
		||||
    log.transports.file.level = -1 as any;   // Suppress all log output.
 | 
			
		||||
  }
 | 
			
		||||
  log.add(CaptureTransport as any, { captureFunc: capture, name, level: minLevel});  // types are off.
 | 
			
		||||
    if (!process.env.VERBOSE) {
 | 
			
		||||
      log.transports.file.level = -1 as any;   // Suppress all log output.
 | 
			
		||||
    }
 | 
			
		||||
    log.add(CaptureTransport as any, { captureFunc: capture, name, level: minLevel});  // types are off.
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    await callback(messages);
 | 
			
		||||
    if (options.waitForFirstLog) {
 | 
			
		||||
      await captureFirstLogPromise;
 | 
			
		||||
    }
 | 
			
		||||
  } finally {
 | 
			
		||||
    log.remove(name);
 | 
			
		||||
    log.transports.file.level = prevLogLevel;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user