(core) When changing a table for a page widget, unset widget-linking to avoid invalid values.

Summary:
Previously, using "Change Widget" allowed one to change the underlying table,
but would keep the linking settings. This could allow invalid settings which
would sometimes lead to JS errors. These manifested in production as
"UserError: Query error: n is not a function".

- Unset linking settings in this case, to avoid invalid values.
- In case invalid values are encountered (e.g. saved previously), treat them as
  unset, to avoid JS errors.
- If an error does occur, report it with a stack trace.

Also, for testing, added 'selectBy' option to gristUtils helpers for using page-widget-picker.

Test Plan: Added test cases for resetting linking, and for ignoring invalid link settings.

Reviewers: alexmojaki

Reviewed By: alexmojaki

Differential Revision: https://phab.getgrist.com/D2993
This commit is contained in:
Dmitry S
2021-08-23 23:28:07 -04:00
parent 427a17d038
commit faa0d9988e
5 changed files with 82 additions and 18 deletions

View File

@@ -1,5 +1,3 @@
/* global window */
var _ = require('underscore');
var ko = require('knockout');
var moment = require('moment-timezone');
@@ -26,6 +24,7 @@ const {copyToClipboard} = require('app/client/lib/copyToClipboard');
const {setTestState} = require('app/client/lib/testState');
const {ExtraRows} = require('app/client/models/DataTableModelWithDiff');
const {createFilterMenu} = require('app/client/ui/ColumnFilterMenu');
const {LinkConfig} = require('app/client/ui/selectBy');
const {encodeObject} = require("app/plugin/objtypes");
/**
@@ -133,10 +132,16 @@ function BaseView(gristDoc, viewSectionModel, options) {
let v = this.viewSection;
let src = v.linkSrcSection();
const filterByAllShown = v.optionsObj.prop('filterByAllShown');
return src.getRowId() ?
LinkingState.create.bind(LinkingState, this.gristDoc,
src, v.linkSrcCol().colId(), v, v.linkTargetCol().colId(), filterByAllShown()) :
null;
if (!src.getRowId()) {
return null;
}
try {
const config = new LinkConfig(v);
return LinkingState.create.bind(LinkingState, this.gristDoc, config, filterByAllShown());
} catch (err) {
console.warn(`Can't create LinkingState: ${err.message}`);
return null;
}
}));
this._linkingFilter = this.autoDispose(ko.computed(() => {
@@ -213,7 +218,7 @@ function BaseView(gristDoc, viewSectionModel, options) {
const linkingFilter = this._linkingFilter();
this._queryRowSource.makeQuery(linkingFilter.filters, linkingFilter.operations, (err) => {
if (this.isDisposed()) { return; }
if (err) { window.gristNotify(`Query error: ${err.message}`); }
if (err) { reportError(err); }
this.onTableLoaded();
});
}));

View File

@@ -46,7 +46,8 @@ function isSummaryOf(summary, detail) {
* value at the cursor. The user can use column filters on srcSection to control what's shown
* in the linked tgtSection.
*/
function LinkingState(gristDoc, srcSection, srcColId, tgtSection, tgtColId, byAllShown) {
function LinkingState(gristDoc, linkConfig, byAllShown) {
const {srcSection, srcColId, tgtSection, tgtCol, tgtColId} = linkConfig;
this._srcSection = srcSection;
let srcTableModel = gristDoc.getTableModel(srcSection.table().tableId());
@@ -66,7 +67,6 @@ function LinkingState(gristDoc, srcSection, srcColId, tgtSection, tgtColId, byAl
// A computed that evaluates to a filter function to use, or null if not filtering. If
// filtering, depends on srcSection.activeRowId().
if (tgtColId) {
const tgtCol = tgtSection.table().columns().all().find(c => c.colId() === tgtColId);
const operations = {[tgtColId]: isRefListType(tgtCol.type()) ? 'intersects' : 'in'};
if (byAllShown) {
// (This is legacy code that isn't currently reachable)