mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Polish Record Cards
Summary: Improvements - Widget and column descriptions are now copied when duplicating a table. - A Grist Plugin API command to open a Record Card is now available. - New Card widgets set initial settings based on those used by their table's Record Card. Fixes - Opening a reference in a Record Card from a Raw Data popup now opens the correct reference. Test Plan: Browser and python tests. Reviewers: jarek Reviewed By: jarek Differential Revision: https://phab.getgrist.com/D4164
This commit is contained in:
parent
11afc08f65
commit
b1f7ca353a
@ -816,5 +816,18 @@ BaseView.prototype._duplicateRows = async function() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseView.prototype.viewSelectedRecordAsCard = function() {
|
||||||
|
if (this.isRecordCardDisabled()) { return; }
|
||||||
|
|
||||||
|
const colRef = this.viewSection.viewFields().at(this.cursor.fieldIndex()).column().id();
|
||||||
|
const rowId = this.viewData.getRowId(this.cursor.rowIndex());
|
||||||
|
const sectionId = this.viewSection.tableRecordCard().id();
|
||||||
|
const anchorUrlState = {hash: {colRef, rowId, sectionId, recordCard: true}};
|
||||||
|
urlState().pushUrl(anchorUrlState, {replace: true}).catch(reportError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseView.prototype.isRecordCardDisabled = function() {
|
||||||
|
return this.viewSection.isTableRecordCardDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = BaseView;
|
module.exports = BaseView;
|
||||||
|
@ -70,6 +70,17 @@ export class CustomView extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async viewAsCard(event: Event) {
|
||||||
|
if (event instanceof KeyboardEvent) {
|
||||||
|
// Ignore the keyboard shortcut if pressed; it's disabled at this time for custom widgets.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(this as unknown as BaseView).viewSelectedRecordAsCard();
|
||||||
|
|
||||||
|
// Move focus back to the app, so that keyboard shortcuts work in the popup.
|
||||||
|
document.querySelector<HTMLElement>('textarea.copypaste.mousetrap')?.focus();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* The HTMLElement embedding the content.
|
* The HTMLElement embedding the content.
|
||||||
|
@ -55,8 +55,6 @@ const {NEW_FILTER_JSON} = require('app/client/models/ColumnFilter');
|
|||||||
const {CombinedStyle} = require("app/client/models/Styles");
|
const {CombinedStyle} = require("app/client/models/Styles");
|
||||||
const {buildRenameColumn} = require('app/client/ui/ColumnTitle');
|
const {buildRenameColumn} = require('app/client/ui/ColumnTitle');
|
||||||
const {makeT} = require('app/client/lib/localization');
|
const {makeT} = require('app/client/lib/localization');
|
||||||
const {reportError} = require('app/client/models/AppModel');
|
|
||||||
const {urlState} = require('app/client/models/gristUrlState');
|
|
||||||
|
|
||||||
const t = makeT('GridView');
|
const t = makeT('GridView');
|
||||||
|
|
||||||
@ -374,16 +372,10 @@ GridView.gridCommands = {
|
|||||||
this.viewSection.rawNumFrozen.setAndSave(action.numFrozen);
|
this.viewSection.rawNumFrozen.setAndSave(action.numFrozen);
|
||||||
},
|
},
|
||||||
viewAsCard() {
|
viewAsCard() {
|
||||||
if (this._isRecordCardDisabled()) { return; }
|
|
||||||
|
|
||||||
const selectedRows = this.selectedRows();
|
const selectedRows = this.selectedRows();
|
||||||
if (selectedRows.length !== 1) { return; }
|
if (selectedRows.length !== 1) { return; }
|
||||||
|
|
||||||
const colRef = this.viewSection.viewFields().at(this.cursor.fieldIndex()).column().id();
|
this.viewSelectedRecordAsCard();
|
||||||
const rowId = selectedRows[0];
|
|
||||||
const sectionId = this.viewSection.tableRecordCard().id();
|
|
||||||
const anchorUrlState = {hash: {colRef, rowId, sectionId, recordCard: true}};
|
|
||||||
urlState().pushUrl(anchorUrlState, {replace: true}).catch(reportError);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1924,14 +1916,13 @@ GridView.prototype.rowContextMenu = function() {
|
|||||||
GridView.prototype._getRowContextMenuOptions = function() {
|
GridView.prototype._getRowContextMenuOptions = function() {
|
||||||
return {
|
return {
|
||||||
...this._getCellContextMenuOptions(),
|
...this._getCellContextMenuOptions(),
|
||||||
disableShowRecordCard: this._isRecordCardDisabled(),
|
disableShowRecordCard: this.isRecordCardDisabled(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
GridView.prototype._isRecordCardDisabled = function() {
|
GridView.prototype.isRecordCardDisabled = function() {
|
||||||
return this.getSelection().onlyAddRowSelected() ||
|
return BaseView.prototype.isRecordCardDisabled.call(this) ||
|
||||||
this.viewSection.isTableRecordCardDisabled() ||
|
this.getSelection().onlyAddRowSelected();
|
||||||
this.viewSection.table().summarySourceTable() !== 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GridView.prototype.cellContextMenu = function() {
|
GridView.prototype.cellContextMenu = function() {
|
||||||
|
@ -206,7 +206,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
private _showGristTour = getUserOrgPrefObs(this.userOrgPrefs, 'showGristTour');
|
private _showGristTour = getUserOrgPrefObs(this.userOrgPrefs, 'showGristTour');
|
||||||
private _seenDocTours = getUserOrgPrefObs(this.userOrgPrefs, 'seenDocTours');
|
private _seenDocTours = getUserOrgPrefObs(this.userOrgPrefs, 'seenDocTours');
|
||||||
private _popupSectionOptions: Observable<PopupSectionOptions | null> = Observable.create(this, null);
|
private _popupSectionOptions: Observable<PopupSectionOptions | null> = Observable.create(this, null);
|
||||||
private _activeContent: Computed<IDocPage | PopupSectionOptions>;
|
private _activeContent: Computed<IDocPage>;
|
||||||
private _docTutorialHolder = Holder.create<DocTutorial>(this);
|
private _docTutorialHolder = Holder.create<DocTutorial>(this);
|
||||||
private _isRickRowing: Observable<boolean> = Observable.create(this, false);
|
private _isRickRowing: Observable<boolean> = Observable.create(this, false);
|
||||||
private _showBackgroundVideoPlayer: Observable<boolean> = Observable.create(this, false);
|
private _showBackgroundVideoPlayer: Observable<boolean> = Observable.create(this, false);
|
||||||
@ -261,7 +261,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
const viewId = this.docModel.views.tableData.findRow(docPage === 'GristDocTour' ? 'name' : 'id', docPage);
|
const viewId = this.docModel.views.tableData.findRow(docPage === 'GristDocTour' ? 'name' : 'id', docPage);
|
||||||
return viewId || use(defaultViewId);
|
return viewId || use(defaultViewId);
|
||||||
});
|
});
|
||||||
this._activeContent = Computed.create(this, use => use(this._popupSectionOptions) ?? use(this.activeViewId));
|
this._activeContent = Computed.create(this, use => use(this.activeViewId));
|
||||||
this.externalSectionId = Computed.create(this, use => {
|
this.externalSectionId = Computed.create(this, use => {
|
||||||
const externalContent = use(this._popupSectionOptions);
|
const externalContent = use(this._popupSectionOptions);
|
||||||
return externalContent ? use(externalContent.viewSection.id) : null;
|
return externalContent ? use(externalContent.viewSection.id) : null;
|
||||||
@ -308,7 +308,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (state.hash.popup || state.hash.recordCard) {
|
if (state.hash.popup || state.hash.recordCard) {
|
||||||
await this.openPopup(state.hash);
|
await this._openPopup(state.hash);
|
||||||
} else {
|
} else {
|
||||||
// Navigate to an anchor if one is present in the url hash.
|
// Navigate to an anchor if one is present in the url hash.
|
||||||
const cursorPos = this._getCursorPosFromHash(state.hash);
|
const cursorPos = this._getCursorPosFromHash(state.hash);
|
||||||
@ -343,7 +343,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.behavioralPromptsManager.showTip(cursor, 'rickRow', {
|
this.behavioralPromptsManager.showTip(cursor, 'rickRow', {
|
||||||
onDispose: () => this.playRickRollVideo(),
|
onDispose: () => this._playRickRollVideo(),
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(reportError);
|
.catch(reportError);
|
||||||
@ -602,7 +602,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
const isPopup = Computed.create(this, use => {
|
const isPopup = Computed.create(this, use => {
|
||||||
return ['data', 'settings'].includes(use(this.activeViewId) as any) // On Raw data or doc settings pages
|
return ['data', 'settings'].includes(use(this.activeViewId) as any) // On Raw data or doc settings pages
|
||||||
|| use(isMaximized) // Layout has a maximized section visible
|
|| use(isMaximized) // Layout has a maximized section visible
|
||||||
|| typeof use(this._activeContent) === 'object'; // We are on show raw data popup
|
|| Boolean(use(this._popupSectionOptions)); // Layout has a popup section visible
|
||||||
});
|
});
|
||||||
return cssViewContentPane(
|
return cssViewContentPane(
|
||||||
testId('gristdoc'),
|
testId('gristdoc'),
|
||||||
@ -623,43 +623,48 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
content === 'settings' ? dom.create(DocSettingsPage, this) :
|
content === 'settings' ? dom.create(DocSettingsPage, this) :
|
||||||
content === 'webhook' ? dom.create(WebhookPage, this) :
|
content === 'webhook' ? dom.create(WebhookPage, this) :
|
||||||
content === 'GristDocTour' ? null :
|
content === 'GristDocTour' ? null :
|
||||||
(typeof content === 'object') ? dom.create(owner => {
|
[
|
||||||
// In case user changes a page, close the popup.
|
dom.create((owner) => {
|
||||||
owner.autoDispose(this.activeViewId.addListener(content.close));
|
this.viewLayout = ViewLayout.create(owner, this, content);
|
||||||
// In case the section is removed, close the popup.
|
this.viewLayout.maximized.addListener(sectionId => {
|
||||||
content.viewSection.autoDispose({dispose: content.close});
|
this.maximizedSectionId.set(sectionId);
|
||||||
|
|
||||||
const {recordCard, rowId} = content.hash;
|
if (sectionId === null && !this._isShowingPopupSection) {
|
||||||
if (recordCard) {
|
// If we didn't navigate to another section in the popup, move focus
|
||||||
if (!rowId || rowId === 'new') {
|
// back to the previous section.
|
||||||
// Should be unreachable, but just to be sure (and to satisfy type checking)...
|
this._focusPreviousSection();
|
||||||
throw new Error('Unable to open Record Card: undefined row id');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return dom.create(RecordCardPopup, {
|
|
||||||
gristDoc: this,
|
|
||||||
rowId,
|
|
||||||
viewSection: content.viewSection,
|
|
||||||
onClose: content.close,
|
|
||||||
});
|
});
|
||||||
} else {
|
owner.onDispose(() => this.viewLayout = null);
|
||||||
return dom.create(RawDataPopup, this, content.viewSection, content.close);
|
return this.viewLayout;
|
||||||
}
|
}),
|
||||||
}) :
|
dom.maybe(this._popupSectionOptions, (popupOptions) => {
|
||||||
dom.create((owner) => {
|
return dom.create((owner) => {
|
||||||
this.viewLayout = ViewLayout.create(owner, this, content);
|
// In case user changes a page, close the popup.
|
||||||
this.viewLayout.maximized.addListener(sectionId => {
|
owner.autoDispose(this.activeViewId.addListener(popupOptions.close));
|
||||||
this.maximizedSectionId.set(sectionId);
|
|
||||||
|
|
||||||
if (sectionId === null && !this._isShowingPopupSection) {
|
// In case the section is removed, close the popup.
|
||||||
// If we didn't navigate to another section in the popup, move focus
|
popupOptions.viewSection.autoDispose({dispose: popupOptions.close});
|
||||||
// back to the previous section.
|
|
||||||
this._focusPreviousSection();
|
const {recordCard, rowId} = popupOptions.hash;
|
||||||
}
|
if (recordCard) {
|
||||||
});
|
if (!rowId || rowId === 'new') {
|
||||||
owner.onDispose(() => this.viewLayout = null);
|
// Should be unreachable, but just to be sure (and to satisfy type checking)...
|
||||||
return this.viewLayout;
|
throw new Error('Unable to open Record Card: undefined row id');
|
||||||
})
|
}
|
||||||
|
|
||||||
|
return dom.create(RecordCardPopup, {
|
||||||
|
gristDoc: this,
|
||||||
|
rowId,
|
||||||
|
viewSection: popupOptions.viewSection,
|
||||||
|
onClose: popupOptions.close,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return dom.create(RawDataPopup, this, popupOptions.viewSection, popupOptions.close);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
]
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
dom.maybe(this._showBackgroundVideoPlayer, () => [
|
dom.maybe(this._showBackgroundVideoPlayer, () => [
|
||||||
@ -1371,10 +1376,36 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
await tableRec.tableName.saveOnly(newTableName);
|
await tableRec.tableName.saveOnly(newTableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates computed with all the data for the given column.
|
||||||
|
*/
|
||||||
|
public columnObserver(owner: IDisposableOwner, tableId: Observable<string>, columnId: Observable<string>) {
|
||||||
|
const tableModel = Computed.create(owner, (use) => this.docModel.dataTables[use(tableId)]);
|
||||||
|
const refreshed = Observable.create(owner, 0);
|
||||||
|
const toggle = () => !refreshed.isDisposed() && refreshed.set(refreshed.get() + 1);
|
||||||
|
const holder = Holder.create(owner);
|
||||||
|
const listener = (tab: TableModel) => {
|
||||||
|
// Now subscribe to any data change in that table.
|
||||||
|
const subs = MultiHolder.create(holder);
|
||||||
|
subs.autoDispose(tab.tableData.dataLoadedEmitter.addListener(toggle));
|
||||||
|
subs.autoDispose(tab.tableData.tableActionEmitter.addListener(toggle));
|
||||||
|
tab.fetch().catch(reportError);
|
||||||
|
};
|
||||||
|
owner.autoDispose(tableModel.addListener(listener));
|
||||||
|
listener(tableModel.get());
|
||||||
|
const values = Computed.create(owner, refreshed, (use) => {
|
||||||
|
const rows = use(tableModel).getAllRows();
|
||||||
|
const colValues = use(tableModel).tableData.getColValues(use(columnId));
|
||||||
|
if (!colValues) { return []; }
|
||||||
|
return rows.map((row, i) => [row, colValues[i]]);
|
||||||
|
});
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens popup with a section data (used by Raw Data view).
|
* Opens popup with a section data (used by Raw Data view).
|
||||||
*/
|
*/
|
||||||
public async openPopup(hash: HashLink) {
|
private async _openPopup(hash: HashLink) {
|
||||||
// We can only open a popup for a section.
|
// We can only open a popup for a section.
|
||||||
if (!hash.sectionId) {
|
if (!hash.sectionId) {
|
||||||
return;
|
return;
|
||||||
@ -1386,13 +1417,17 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
if (this.viewModel.viewSections.peek().peek().some(s => s.id.peek() === hash.sectionId)) {
|
if (this.viewModel.viewSections.peek().peek().some(s => s.id.peek() === hash.sectionId)) {
|
||||||
this.viewModel.activeSectionId(hash.sectionId);
|
this.viewModel.activeSectionId(hash.sectionId);
|
||||||
// If the anchor link is valid, set the cursor.
|
// If the anchor link is valid, set the cursor.
|
||||||
if (hash.colRef && hash.rowId) {
|
if (hash.colRef || hash.rowId) {
|
||||||
const activeSection = this.viewModel.activeSection.peek();
|
const activeSection = this.viewModel.activeSection.peek();
|
||||||
const fieldIndex = activeSection.viewFields.peek().all().findIndex(f => f.colRef.peek() === hash.colRef);
|
const {rowId} = hash;
|
||||||
if (fieldIndex >= 0) {
|
let fieldIndex = undefined;
|
||||||
const view = await this._waitForView(activeSection);
|
if (hash.colRef) {
|
||||||
view?.setCursorPos({rowId: hash.rowId, fieldIndex});
|
const maybeFieldIndex = activeSection.viewFields.peek().all()
|
||||||
|
.findIndex(f => f.colRef.peek() === hash.colRef);
|
||||||
|
if (maybeFieldIndex !== -1) { fieldIndex = maybeFieldIndex; }
|
||||||
}
|
}
|
||||||
|
const view = await this._waitForView(activeSection);
|
||||||
|
view?.setCursorPos({rowId, fieldIndex});
|
||||||
}
|
}
|
||||||
this.viewLayout?.maximized.set(hash.sectionId);
|
this.viewLayout?.maximized.set(hash.sectionId);
|
||||||
return;
|
return;
|
||||||
@ -1451,7 +1486,7 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
/**
|
/**
|
||||||
* Starts playing the music video for Never Gonna Give You Up in the background.
|
* Starts playing the music video for Never Gonna Give You Up in the background.
|
||||||
*/
|
*/
|
||||||
public async playRickRollVideo() {
|
private async _playRickRollVideo() {
|
||||||
const backgroundVideoPlayer = this._backgroundVideoPlayerHolder.get();
|
const backgroundVideoPlayer = this._backgroundVideoPlayerHolder.get();
|
||||||
if (!backgroundVideoPlayer) {
|
if (!backgroundVideoPlayer) {
|
||||||
return;
|
return;
|
||||||
@ -1487,32 +1522,6 @@ export class GristDoc extends DisposableWithEvents {
|
|||||||
this._showBackgroundVideoPlayer.set(false);
|
this._showBackgroundVideoPlayer.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates computed with all the data for the given column.
|
|
||||||
*/
|
|
||||||
public columnObserver(owner: IDisposableOwner, tableId: Observable<string>, columnId: Observable<string>) {
|
|
||||||
const tableModel = Computed.create(owner, (use) => this.docModel.dataTables[use(tableId)]);
|
|
||||||
const refreshed = Observable.create(owner, 0);
|
|
||||||
const toggle = () => !refreshed.isDisposed() && refreshed.set(refreshed.get() + 1);
|
|
||||||
const holder = Holder.create(owner);
|
|
||||||
const listener = (tab: TableModel) => {
|
|
||||||
// Now subscribe to any data change in that table.
|
|
||||||
const subs = MultiHolder.create(holder);
|
|
||||||
subs.autoDispose(tab.tableData.dataLoadedEmitter.addListener(toggle));
|
|
||||||
subs.autoDispose(tab.tableData.tableActionEmitter.addListener(toggle));
|
|
||||||
tab.fetch().catch(reportError);
|
|
||||||
};
|
|
||||||
owner.autoDispose(tableModel.addListener(listener));
|
|
||||||
listener(tableModel.get());
|
|
||||||
const values = Computed.create(owner, refreshed, (use) => {
|
|
||||||
const rows = use(tableModel).getAllRows();
|
|
||||||
const colValues = use(tableModel).tableData.getColValues(use(columnId));
|
|
||||||
if (!colValues) { return []; }
|
|
||||||
return rows.map((row, i) => [row, colValues[i]]);
|
|
||||||
});
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _focusPreviousSection() {
|
private _focusPreviousSection() {
|
||||||
const prevSectionId = this._prevSectionId;
|
const prevSectionId = this._prevSectionId;
|
||||||
if (!prevSectionId) { return; }
|
if (!prevSectionId) { return; }
|
||||||
@ -1890,26 +1899,25 @@ async function finalizeAnchor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cssViewContentPane = styled('div', `
|
const cssViewContentPane = styled('div', `
|
||||||
--view-content-page-margin: 12px;
|
--view-content-page-padding: 12px;
|
||||||
flex: auto;
|
flex: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-width: 240px;
|
min-width: 240px;
|
||||||
margin: var(--view-content-page-margin, 12px);
|
padding: var(--view-content-page-padding, 12px);
|
||||||
@media ${mediaSmall} {
|
@media ${mediaSmall} {
|
||||||
& {
|
& {
|
||||||
margin: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media print {
|
@media print {
|
||||||
& {
|
& {
|
||||||
margin: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&-contents {
|
&-contents {
|
||||||
margin: 0px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
@ -501,7 +501,7 @@ export class LayoutEditor extends Disposable {
|
|||||||
handles: isWidth ? 'e' : 's',
|
handles: isWidth ? 'e' : 's',
|
||||||
start: this.onResizeStart.bind(this, helperObj, isWidth),
|
start: this.onResizeStart.bind(this, helperObj, isWidth),
|
||||||
resize: this.onResizeMove.bind(this, helperObj, isWidth),
|
resize: this.onResizeMove.bind(this, helperObj, isWidth),
|
||||||
stop: this.triggerUserEditStop.bind(this)
|
stop: this.triggerUserEditStop.bind(this),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public unmakeResizable(box: LayoutBox) {
|
public unmakeResizable(box: LayoutBox) {
|
||||||
|
@ -1179,7 +1179,7 @@ const cssCollapsedTray = styled('div.collapsed_layout', `
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: height 0.2s;
|
transition: height 0.2s;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: calc(-1 * var(--view-content-page-margin, 12px));
|
margin: calc(-1 * var(--view-content-page-padding, 12px));
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: ${theme.pageBg};
|
background-color: ${theme.pageBg};
|
||||||
|
@ -4,7 +4,7 @@ import {DocumentUsage} from 'app/client/components/DocumentUsage';
|
|||||||
import {GristDoc} from 'app/client/components/GristDoc';
|
import {GristDoc} from 'app/client/components/GristDoc';
|
||||||
import {printViewSection} from 'app/client/components/Printing';
|
import {printViewSection} from 'app/client/components/Printing';
|
||||||
import {ViewSectionHelper} from 'app/client/components/ViewLayout';
|
import {ViewSectionHelper} from 'app/client/components/ViewLayout';
|
||||||
import {mediaSmall, theme} from 'app/client/ui2018/cssVars';
|
import {mediaSmall, theme, vars} from 'app/client/ui2018/cssVars';
|
||||||
import {icon} from 'app/client/ui2018/icons';
|
import {icon} from 'app/client/ui2018/icons';
|
||||||
import {Computed, Disposable, dom, fromKo, makeTestId, Observable, styled} from 'grainjs';
|
import {Computed, Disposable, dom, fromKo, makeTestId, Observable, styled} from 'grainjs';
|
||||||
import {reportError} from 'app/client/models/errors';
|
import {reportError} from 'app/client/models/errors';
|
||||||
@ -115,7 +115,8 @@ export class RawDataPopup extends Disposable {
|
|||||||
const cssContainer = styled('div', `
|
const cssContainer = styled('div', `
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
inset: 0px;
|
||||||
|
position: absolute;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssPage = styled('div', `
|
const cssPage = styled('div', `
|
||||||
@ -132,10 +133,9 @@ const cssPage = styled('div', `
|
|||||||
export const cssOverlay = styled('div', `
|
export const cssOverlay = styled('div', `
|
||||||
background-color: ${theme.modalBackdrop};
|
background-color: ${theme.modalBackdrop};
|
||||||
inset: 0px;
|
inset: 0px;
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px 56px 20px 56px;
|
padding: 20px 56px 20px 56px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
z-index: ${vars.popupSectionBackdropZIndex};
|
||||||
@media ${mediaSmall} {
|
@media ${mediaSmall} {
|
||||||
& {
|
& {
|
||||||
padding: 22px;
|
padding: 22px;
|
||||||
|
@ -50,6 +50,7 @@ export class RecordCardPopup extends DisposableWithEvents {
|
|||||||
focusable: false,
|
focusable: false,
|
||||||
renamable: false,
|
renamable: false,
|
||||||
}),
|
}),
|
||||||
|
testId('wrapper'),
|
||||||
),
|
),
|
||||||
cssCloseButton('CrossBig',
|
cssCloseButton('CrossBig',
|
||||||
dom.on('click', () => this._handleClose()),
|
dom.on('click', () => this._handleClose()),
|
||||||
|
@ -544,6 +544,7 @@ export class WidgetAPIImpl implements WidgetAPI {
|
|||||||
const COMMAND_MINIMUM_ACCESS_LEVELS: Map<CommandName, AccessLevel> = new Map([
|
const COMMAND_MINIMUM_ACCESS_LEVELS: Map<CommandName, AccessLevel> = new Map([
|
||||||
['undo', AccessLevel.full],
|
['undo', AccessLevel.full],
|
||||||
['redo', AccessLevel.full],
|
['redo', AccessLevel.full],
|
||||||
|
['viewAsCard', AccessLevel.read_table],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export class CommandAPI {
|
export class CommandAPI {
|
||||||
|
2
app/client/declarations.d.ts
vendored
2
app/client/declarations.d.ts
vendored
@ -77,6 +77,8 @@ declare module "app/client/components/BaseView" {
|
|||||||
public moveEditRowToCursor(): DataRowModel;
|
public moveEditRowToCursor(): DataRowModel;
|
||||||
public scrollToCursor(sync: boolean): Promise<void>;
|
public scrollToCursor(sync: boolean): Promise<void>;
|
||||||
public getAnchorLinkForSection(sectionId: number): IGristUrlState;
|
public getAnchorLinkForSection(sectionId: number): IGristUrlState;
|
||||||
|
public viewSelectedRecordAsCard(): void;
|
||||||
|
public isRecordCardDisabled(): boolean;
|
||||||
}
|
}
|
||||||
export = BaseView;
|
export = BaseView;
|
||||||
}
|
}
|
||||||
|
@ -99,10 +99,7 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO
|
|||||||
/** True if this section is disabled. Currently only used by Record Card sections. */
|
/** True if this section is disabled. Currently only used by Record Card sections. */
|
||||||
disabled: modelUtil.KoSaveableObservable<boolean>;
|
disabled: modelUtil.KoSaveableObservable<boolean>;
|
||||||
|
|
||||||
/**
|
/** True if the Record Card section of this section's table is disabled. */
|
||||||
* True if the Record Card section of this section's table is disabled. Shortcut for
|
|
||||||
* `this.tableRecordCard().disabled()`.
|
|
||||||
*/
|
|
||||||
isTableRecordCardDisabled: ko.Computed<boolean>;
|
isTableRecordCardDisabled: ko.Computed<boolean>;
|
||||||
|
|
||||||
isVirtual: ko.Computed<boolean>;
|
isVirtual: ko.Computed<boolean>;
|
||||||
@ -485,7 +482,8 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel):
|
|||||||
this.isRecordCard = this.autoDispose(ko.pureComputed(() =>
|
this.isRecordCard = this.autoDispose(ko.pureComputed(() =>
|
||||||
this.table().recordCardViewSectionRef() === this.id()));
|
this.table().recordCardViewSectionRef() === this.id()));
|
||||||
this.disabled = modelUtil.fieldWithDefault(this.optionsObj.prop('disabled'), false);
|
this.disabled = modelUtil.fieldWithDefault(this.optionsObj.prop('disabled'), false);
|
||||||
this.isTableRecordCardDisabled = ko.pureComputed(() => this.tableRecordCard().disabled());
|
this.isTableRecordCardDisabled = this.autoDispose(ko.pureComputed(() => this.tableRecordCard().disabled() ||
|
||||||
|
this.table().summarySourceTable() !== 0));
|
||||||
|
|
||||||
this.isVirtual = this.autoDispose(ko.pureComputed(() => typeof this.id() === 'string'));
|
this.isVirtual = this.autoDispose(ko.pureComputed(() => typeof this.id() === 'string'));
|
||||||
|
|
||||||
|
@ -72,11 +72,9 @@ export function makeViewLayoutMenu(viewSection: ViewSectionRec, isReadonly: bool
|
|||||||
dom.maybe(showRawData,
|
dom.maybe(showRawData,
|
||||||
() => menuItemLink(
|
() => menuItemLink(
|
||||||
{ href: rawUrl}, t("Show raw data"), testId('show-raw-data'),
|
{ href: rawUrl}, t("Show raw data"), testId('show-raw-data'),
|
||||||
dom.on('click', (ev) => {
|
dom.on('click', () => {
|
||||||
// Replace the current URL so that the back button works as expected (it navigates back from
|
// Replace the current URL so that the back button works as expected (it navigates back from
|
||||||
// the current page).
|
// the current page).
|
||||||
ev.stopImmediatePropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
urlState().pushUrl(anchorUrlState, { replace: true }).catch(reportError);
|
urlState().pushUrl(anchorUrlState, { replace: true }).catch(reportError);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -128,11 +126,9 @@ export function makeCollapsedLayoutMenu(viewSection: ViewSectionRec, gristDoc: G
|
|||||||
dom.maybe((use) => !use(viewSection.isRaw) && !isSinglePage && !use(gristDoc.maximizedSectionId),
|
dom.maybe((use) => !use(viewSection.isRaw) && !isSinglePage && !use(gristDoc.maximizedSectionId),
|
||||||
() => menuItemLink(
|
() => menuItemLink(
|
||||||
{ href: rawUrl}, t("Show raw data"), testId('show-raw-data'),
|
{ href: rawUrl}, t("Show raw data"), testId('show-raw-data'),
|
||||||
dom.on('click', (ev) => {
|
dom.on('click', () => {
|
||||||
// Replace the current URL so that the back button works as expected (it navigates back from
|
// Replace the current URL so that the back button works as expected (it navigates back from
|
||||||
// the current page).
|
// the current page).
|
||||||
ev.stopImmediatePropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
urlState().pushUrl(anchorUrlState, { replace: true }).catch(reportError);
|
urlState().pushUrl(anchorUrlState, { replace: true }).catch(reportError);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -137,6 +137,7 @@ export const vars = {
|
|||||||
|
|
||||||
/* Z indexes */
|
/* Z indexes */
|
||||||
insertColumnLineZIndex: new CustomProp('insert-column-line-z-index', '20'),
|
insertColumnLineZIndex: new CustomProp('insert-column-line-z-index', '20'),
|
||||||
|
popupSectionBackdropZIndex: new CustomProp('popup-section-backdrop-z-index', '100'),
|
||||||
menuZIndex: new CustomProp('menu-z-index', '999'),
|
menuZIndex: new CustomProp('menu-z-index', '999'),
|
||||||
modalZIndex: new CustomProp('modal-z-index', '999'),
|
modalZIndex: new CustomProp('modal-z-index', '999'),
|
||||||
onboardingBackdropZIndex: new CustomProp('onboarding-backdrop-z-index', '999'),
|
onboardingBackdropZIndex: new CustomProp('onboarding-backdrop-z-index', '999'),
|
||||||
|
@ -119,7 +119,7 @@ const cssOptions = styled('div', `
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 48px;
|
top: 48px;
|
||||||
z-index: 1;
|
z-index: ${vars.menuZIndex};
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -1599,6 +1599,14 @@ class TestUserActions(test_engine.EngineTestCase):
|
|||||||
'formula': '$A == "Foo"',
|
'formula': '$A == "Foo"',
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
# Add a column and widget description.
|
||||||
|
self.apply_user_action(['UpdateRecord', '_grist_Tables_column', 23, {
|
||||||
|
'description': 'A column description.',
|
||||||
|
}])
|
||||||
|
self.apply_user_action(['UpdateRecord', '_grist_Views_section', 2, {
|
||||||
|
'description': 'A widget description.',
|
||||||
|
}])
|
||||||
|
|
||||||
# Duplicate Table1 as Foo without including any of its data.
|
# Duplicate Table1 as Foo without including any of its data.
|
||||||
self.apply_user_action(['DuplicateTable', 'Table1', 'Foo', False])
|
self.apply_user_action(['DuplicateTable', 'Table1', 'Foo', False])
|
||||||
|
|
||||||
@ -1642,6 +1650,16 @@ class TestUserActions(test_engine.EngineTestCase):
|
|||||||
["id", "A", "B", "C", "D", "E", "F", "G", "H", "gristHelper_ConditionalRule",
|
["id", "A", "B", "C", "D", "E", "F", "G", "H", "gristHelper_ConditionalRule",
|
||||||
"gristHelper_RowConditionalRule", "manualSort"],
|
"gristHelper_RowConditionalRule", "manualSort"],
|
||||||
])
|
])
|
||||||
|
self.assertTableData('_grist_Tables_column', rows='subset', cols='subset', data=[
|
||||||
|
['id', 'description'],
|
||||||
|
[23, 'A column description.'],
|
||||||
|
[34, 'A column description.'],
|
||||||
|
])
|
||||||
|
self.assertTableData('_grist_Views_section', rows='subset', cols='subset', data=[
|
||||||
|
['id', 'description'],
|
||||||
|
[2, 'A widget description.'],
|
||||||
|
[4, 'A widget description.'],
|
||||||
|
])
|
||||||
|
|
||||||
# Duplicate Table1 as FooData and include all of its data.
|
# Duplicate Table1 as FooData and include all of its data.
|
||||||
self.apply_user_action(['DuplicateTable', 'Table1', 'FooData', True])
|
self.apply_user_action(['DuplicateTable', 'Table1', 'FooData', True])
|
||||||
@ -1686,7 +1704,7 @@ class TestUserActions(test_engine.EngineTestCase):
|
|||||||
duplicated_times = self.engine.fetch_table('FooData').columns['E']
|
duplicated_times = self.engine.fetch_table('FooData').columns['E']
|
||||||
self.assertEqual(existing_times, duplicated_times)
|
self.assertEqual(existing_times, duplicated_times)
|
||||||
|
|
||||||
def test_duplicate_table2(self):
|
def test_duplicate_table_untie_col_id_bug(self):
|
||||||
# This test case verifies a bug fix: when a column doesn't match its label despite
|
# This test case verifies a bug fix: when a column doesn't match its label despite
|
||||||
# untieColIdFromLabel being False (which is possible), ensure that duplicating still works.
|
# untieColIdFromLabel being False (which is possible), ensure that duplicating still works.
|
||||||
|
|
||||||
@ -1703,3 +1721,53 @@ class TestUserActions(test_engine.EngineTestCase):
|
|||||||
self.apply_user_action(['DuplicateTable', 'Table1', 'Foo', True])
|
self.apply_user_action(['DuplicateTable', 'Table1', 'Foo', True])
|
||||||
self.assertTableData('Table1', data=[["id", "State2", 'manualSort'], [1, 'NY', 1.0]])
|
self.assertTableData('Table1', data=[["id", "State2", 'manualSort'], [1, 'NY', 1.0]])
|
||||||
self.assertTableData('Foo', data=[["id", "State2", 'manualSort'], [1, 'NY', 1.0]])
|
self.assertTableData('Foo', data=[["id", "State2", 'manualSort'], [1, 'NY', 1.0]])
|
||||||
|
|
||||||
|
def test_duplicate_table_record_card(self):
|
||||||
|
self.load_sample(self.sample)
|
||||||
|
self.apply_user_action(['AddEmptyTable', None])
|
||||||
|
self.apply_user_action(['AddColumn', 'Table1', None, {
|
||||||
|
'type': 'Ref:Table1',
|
||||||
|
'visibleCol': 23,
|
||||||
|
}])
|
||||||
|
self.apply_user_action(['AddColumn', 'Table1', None, {
|
||||||
|
'type': 'RefList:Table1',
|
||||||
|
'visibleCol': 24,
|
||||||
|
}])
|
||||||
|
self.apply_user_action(['BulkUpdateRecord', '_grist_Views_section_field', [11, 13], {
|
||||||
|
'visibleCol': [23, 24],
|
||||||
|
}])
|
||||||
|
self.apply_user_action(['UpdateRecord', '_grist_Views_section', 3, {
|
||||||
|
'layoutSpec': '{"children":[{"children":[{"leaf":7},{"leaf":8}]},{"leaf":9},{"leaf":11}]}',
|
||||||
|
'options': '{"verticalGridlines":true,"horizontalGridlines":true,"zebraStripes":false,' +
|
||||||
|
'"customView":"","numFrozen":0,"disabled":true}',
|
||||||
|
'theme': 'compact',
|
||||||
|
}])
|
||||||
|
self.apply_user_action(['DuplicateTable', 'Table1', 'Foo', False])
|
||||||
|
|
||||||
|
self.assertTableData('_grist_Views_section', rows="subset", cols="subset", data=[
|
||||||
|
["id", "parentId", "tableRef", "layoutSpec", "options", "theme"],
|
||||||
|
# The original record card section.
|
||||||
|
[3, 0, 2, '{"children":[{"children":[{"leaf":7},{"leaf":8}]},{"leaf":9},{"leaf":11}]}',
|
||||||
|
'{"verticalGridlines":true,"horizontalGridlines":true,"zebraStripes":false,' +
|
||||||
|
'"customView":"","numFrozen":0,"disabled":true}', 'compact'],
|
||||||
|
# The duplicated record card section.
|
||||||
|
[5, 0, 3,
|
||||||
|
'{"children": [{"children": [{"leaf": 19}, {"leaf": 20}]}, {"leaf": 21}, ' +
|
||||||
|
'{"leaf": 22}]}',
|
||||||
|
'{"verticalGridlines":true,"horizontalGridlines":true,"zebraStripes":false,' +
|
||||||
|
'"customView":"","numFrozen":0,"disabled":true}', 'compact'],
|
||||||
|
])
|
||||||
|
self.assertTableData('_grist_Views_section_field', rows="subset", cols="subset", data=[
|
||||||
|
["id", "parentId", "parentPos", "visibleCol"],
|
||||||
|
# The original record card fields.
|
||||||
|
[7, 3, 7.0, 0],
|
||||||
|
[8, 3, 8.0, 0],
|
||||||
|
[9, 3, 9.0, 0],
|
||||||
|
[11, 3, 11.0, 23],
|
||||||
|
[13, 3, 13.0, 24],
|
||||||
|
[19, 5, 6.5, 0],
|
||||||
|
[20, 5, 7.5, 0],
|
||||||
|
[21, 5, 8.5, 0],
|
||||||
|
[22, 5, 10.5, 29],
|
||||||
|
[23, 5, 12.5, 30],
|
||||||
|
])
|
||||||
|
@ -921,9 +921,10 @@
|
|||||||
|
|
||||||
// Record card widget
|
// Record card widget
|
||||||
["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}],
|
["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}],
|
||||||
|
["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 3}],
|
||||||
["AddRecord", "_grist_Views_section_field", 3, {"colRef": 34, "parentId": 3, "parentPos": 3.0}],
|
["AddRecord", "_grist_Views_section_field", 3, {"colRef": 34, "parentId": 3, "parentPos": 3.0}],
|
||||||
|
|
||||||
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2, "recordCardViewSectionRef": 3}],
|
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2}],
|
||||||
|
|
||||||
// Actions generated from AddColumn.
|
// Actions generated from AddColumn.
|
||||||
["AddColumn", "Bar", "world",
|
["AddColumn", "Bar", "world",
|
||||||
@ -937,7 +938,7 @@
|
|||||||
],
|
],
|
||||||
"direct": [true, true, true, true, true, true, true,
|
"direct": [true, true, true, true, true, true, true,
|
||||||
true, true, true, true, true, true, true,
|
true, true, true, true, true, true, true,
|
||||||
true, true, true],
|
true, true, true, true],
|
||||||
"undo": [
|
"undo": [
|
||||||
["RemoveTable", "Bar"],
|
["RemoveTable", "Bar"],
|
||||||
["RemoveRecord", "_grist_Tables", 4],
|
["RemoveRecord", "_grist_Tables", 4],
|
||||||
@ -950,8 +951,9 @@
|
|||||||
["RemoveRecord", "_grist_Views_section", 2],
|
["RemoveRecord", "_grist_Views_section", 2],
|
||||||
["RemoveRecord", "_grist_Views_section_field", 2],
|
["RemoveRecord", "_grist_Views_section_field", 2],
|
||||||
["RemoveRecord", "_grist_Views_section", 3],
|
["RemoveRecord", "_grist_Views_section", 3],
|
||||||
|
["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 0}],
|
||||||
["RemoveRecord", "_grist_Views_section_field", 3],
|
["RemoveRecord", "_grist_Views_section_field", 3],
|
||||||
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}],
|
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0}],
|
||||||
["RemoveColumn", "Bar", "world"],
|
["RemoveColumn", "Bar", "world"],
|
||||||
["RemoveRecord", "_grist_Tables_column", 35],
|
["RemoveRecord", "_grist_Tables_column", 35],
|
||||||
["RemoveRecord", "_grist_Views_section_field", 4],
|
["RemoveRecord", "_grist_Views_section_field", 4],
|
||||||
@ -1267,15 +1269,16 @@
|
|||||||
["AddRecord", "_grist_Views_section", 2, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 4, "title": ""}],
|
["AddRecord", "_grist_Views_section", 2, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 4, "title": ""}],
|
||||||
["BulkAddRecord", "_grist_Views_section_field", [3, 4], {"colRef": [31, 32], "parentId": [2, 2], "parentPos": [3.0, 4.0]}],
|
["BulkAddRecord", "_grist_Views_section_field", [3, 4], {"colRef": [31, 32], "parentId": [2, 2], "parentPos": [3.0, 4.0]}],
|
||||||
["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}],
|
["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}],
|
||||||
|
["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 3}],
|
||||||
["BulkAddRecord", "_grist_Views_section_field", [5, 6], {"colRef": [31, 32], "parentId": [3, 3], "parentPos": [5.0, 6.0]}],
|
["BulkAddRecord", "_grist_Views_section_field", [5, 6], {"colRef": [31, 32], "parentId": [3, 3], "parentPos": [5.0, 6.0]}],
|
||||||
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2, "recordCardViewSectionRef": 3}],
|
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2}],
|
||||||
["BulkRemoveRecord", "_grist_Views_section_field", [1, 3, 5]],
|
["BulkRemoveRecord", "_grist_Views_section_field", [1, 3, 5]],
|
||||||
["RemoveRecord", "_grist_Tables_column", 31],
|
["RemoveRecord", "_grist_Tables_column", 31],
|
||||||
["RemoveColumn", "ViewTest", "hello"]
|
["RemoveColumn", "ViewTest", "hello"]
|
||||||
|
|
||||||
],
|
],
|
||||||
"direct": [true, true, true, true, true, true, true, true, true,
|
"direct": [true, true, true, true, true, true, true, true, true,
|
||||||
true, true, true, true, true, true, true],
|
true, true, true, true, true, true, true, true],
|
||||||
"undo": [
|
"undo": [
|
||||||
["RemoveTable", "ViewTest"],
|
["RemoveTable", "ViewTest"],
|
||||||
["RemoveRecord", "_grist_Tables", 4],
|
["RemoveRecord", "_grist_Tables", 4],
|
||||||
@ -1288,8 +1291,9 @@
|
|||||||
["RemoveRecord", "_grist_Views_section", 2],
|
["RemoveRecord", "_grist_Views_section", 2],
|
||||||
["BulkRemoveRecord", "_grist_Views_section_field", [3, 4]],
|
["BulkRemoveRecord", "_grist_Views_section_field", [3, 4]],
|
||||||
["RemoveRecord", "_grist_Views_section", 3],
|
["RemoveRecord", "_grist_Views_section", 3],
|
||||||
|
["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 0}],
|
||||||
["BulkRemoveRecord", "_grist_Views_section_field", [5, 6]],
|
["BulkRemoveRecord", "_grist_Views_section_field", [5, 6]],
|
||||||
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}],
|
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0}],
|
||||||
["BulkAddRecord", "_grist_Views_section_field", [1, 3, 5],
|
["BulkAddRecord", "_grist_Views_section_field", [1, 3, 5],
|
||||||
{"colRef": [31, 31, 31], "parentId": [1, 2, 3], "parentPos": [1.0, 3.0, 5.0]}],
|
{"colRef": [31, 31, 31], "parentId": [1, 2, 3], "parentPos": [1.0, 3.0, 5.0]}],
|
||||||
["AddRecord", "_grist_Tables_column", 31,
|
["AddRecord", "_grist_Tables_column", 31,
|
||||||
@ -2213,7 +2217,8 @@
|
|||||||
"parentId": 1, "parentKey": "record", "sortColRefs": "[]", "title": ""}],
|
"parentId": 1, "parentKey": "record", "sortColRefs": "[]", "title": ""}],
|
||||||
["AddRecord", "_grist_Views_section", 2, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 4, "title": ""}],
|
["AddRecord", "_grist_Views_section", 2, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 4, "title": ""}],
|
||||||
["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}],
|
["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}],
|
||||||
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2, "recordCardViewSectionRef": 3}],
|
["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 3}],
|
||||||
|
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2}],
|
||||||
["AddTable", "Bar", [
|
["AddTable", "Bar", [
|
||||||
{"id": "manualSort", "formula": "", "isFormula": false, "type": "ManualSortPos"},
|
{"id": "manualSort", "formula": "", "isFormula": false, "type": "ManualSortPos"},
|
||||||
{"isFormula": false, "formula": "", "type": "Text", "id": "hello"},
|
{"isFormula": false, "formula": "", "type": "Text", "id": "hello"},
|
||||||
@ -2245,15 +2250,16 @@
|
|||||||
["AddRecord", "_grist_Views_section", 5, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 5, "title": ""}],
|
["AddRecord", "_grist_Views_section", 5, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 5, "title": ""}],
|
||||||
["BulkAddRecord", "_grist_Views_section_field", [4, 5, 6], {"colRef": [32, 33, 34], "parentId": [5, 5, 5], "parentPos": [4.0, 5.0, 6.0]}],
|
["BulkAddRecord", "_grist_Views_section_field", [4, 5, 6], {"colRef": [32, 33, 34], "parentId": [5, 5, 5], "parentPos": [4.0, 5.0, 6.0]}],
|
||||||
["AddRecord", "_grist_Views_section", 6, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 5, "title": ""}],
|
["AddRecord", "_grist_Views_section", 6, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 5, "title": ""}],
|
||||||
|
["UpdateRecord", "_grist_Tables", 5, {"recordCardViewSectionRef": 6}],
|
||||||
["BulkAddRecord", "_grist_Views_section_field", [7, 8, 9], {"colRef": [32, 33, 34], "parentId": [6, 6, 6], "parentPos": [7.0, 8.0, 9.0]}],
|
["BulkAddRecord", "_grist_Views_section_field", [7, 8, 9], {"colRef": [32, 33, 34], "parentId": [6, 6, 6], "parentPos": [7.0, 8.0, 9.0]}],
|
||||||
["UpdateRecord", "_grist_Tables", 5, {"primaryViewId": 2, "rawViewSectionRef": 5, "recordCardViewSectionRef": 6}],
|
["UpdateRecord", "_grist_Tables", 5, {"primaryViewId": 2, "rawViewSectionRef": 5}],
|
||||||
["AddRecord", "Bar", 1, {"foo": 0, "hello": "a", "manualSort": 1.0}],
|
["AddRecord", "Bar", 1, {"foo": 0, "hello": "a", "manualSort": 1.0}],
|
||||||
["AddRecord", "Bar", 2, {"foo": 1, "hello": "b", "manualSort": 2.0}],
|
["AddRecord", "Bar", 2, {"foo": 1, "hello": "b", "manualSort": 2.0}],
|
||||||
["AddRecord", "Bar", 3, {"foo": 1, "hello": "c", "manualSort": 3.0}],
|
["AddRecord", "Bar", 3, {"foo": 1, "hello": "c", "manualSort": 3.0}],
|
||||||
["BulkUpdateRecord", "Bar", [1, 2, 3], {"world": ["A", "B", "C"]}]
|
["BulkUpdateRecord", "Bar", [1, 2, 3], {"world": ["A", "B", "C"]}]
|
||||||
],
|
],
|
||||||
"direct": [true, true, true, true, true, true, true, true,
|
"direct": [true, true, true, true, true, true, true, true,
|
||||||
true, true, true, true, true, true,
|
true, true, true, true, true, true, true, true,
|
||||||
true, true, true, true, true, true, true, true, true,
|
true, true, true, true, true, true, true, true, true,
|
||||||
true, true, true, false],
|
true, true, true, false],
|
||||||
"undo": [
|
"undo": [
|
||||||
@ -2266,7 +2272,8 @@
|
|||||||
["RemoveRecord", "_grist_Views_section", 1],
|
["RemoveRecord", "_grist_Views_section", 1],
|
||||||
["RemoveRecord", "_grist_Views_section", 2],
|
["RemoveRecord", "_grist_Views_section", 2],
|
||||||
["RemoveRecord", "_grist_Views_section", 3],
|
["RemoveRecord", "_grist_Views_section", 3],
|
||||||
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}],
|
["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 0}],
|
||||||
|
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0}],
|
||||||
["RemoveTable", "Bar"],
|
["RemoveTable", "Bar"],
|
||||||
["RemoveRecord", "_grist_Tables", 5],
|
["RemoveRecord", "_grist_Tables", 5],
|
||||||
["BulkRemoveRecord", "_grist_Tables_column", [31,32,33,34]],
|
["BulkRemoveRecord", "_grist_Tables_column", [31,32,33,34]],
|
||||||
@ -2278,8 +2285,9 @@
|
|||||||
["RemoveRecord", "_grist_Views_section", 5],
|
["RemoveRecord", "_grist_Views_section", 5],
|
||||||
["BulkRemoveRecord", "_grist_Views_section_field", [4, 5, 6]],
|
["BulkRemoveRecord", "_grist_Views_section_field", [4, 5, 6]],
|
||||||
["RemoveRecord", "_grist_Views_section", 6],
|
["RemoveRecord", "_grist_Views_section", 6],
|
||||||
|
["UpdateRecord", "_grist_Tables", 5, {"recordCardViewSectionRef": 0}],
|
||||||
["BulkRemoveRecord", "_grist_Views_section_field", [7, 8, 9]],
|
["BulkRemoveRecord", "_grist_Views_section_field", [7, 8, 9]],
|
||||||
["UpdateRecord", "_grist_Tables", 5, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}],
|
["UpdateRecord", "_grist_Tables", 5, {"primaryViewId": 0, "rawViewSectionRef": 0}],
|
||||||
["RemoveRecord", "Bar", 1],
|
["RemoveRecord", "Bar", 1],
|
||||||
["RemoveRecord", "Bar", 2],
|
["RemoveRecord", "Bar", 2],
|
||||||
["RemoveRecord", "Bar", 3]
|
["RemoveRecord", "Bar", 3]
|
||||||
@ -2355,9 +2363,10 @@
|
|||||||
// Record card widget
|
// Record card widget
|
||||||
["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}],
|
["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}],
|
||||||
// As part of adding a table, we also set the primaryViewId.
|
// As part of adding a table, we also set the primaryViewId.
|
||||||
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2, "recordCardViewSectionRef": 3}]
|
["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 3}],
|
||||||
|
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2}]
|
||||||
],
|
],
|
||||||
"direct": [true, true, true, true, true, true, true, true, true, true],
|
"direct": [true, true, true, true, true, true, true, true, true, true, true],
|
||||||
"undo": [
|
"undo": [
|
||||||
["RemoveTable", "Foo"],
|
["RemoveTable", "Foo"],
|
||||||
["RemoveRecord", "_grist_Tables", 4],
|
["RemoveRecord", "_grist_Tables", 4],
|
||||||
@ -2368,7 +2377,8 @@
|
|||||||
["RemoveRecord", "_grist_Views_section", 1],
|
["RemoveRecord", "_grist_Views_section", 1],
|
||||||
["RemoveRecord", "_grist_Views_section", 2],
|
["RemoveRecord", "_grist_Views_section", 2],
|
||||||
["RemoveRecord", "_grist_Views_section", 3],
|
["RemoveRecord", "_grist_Views_section", 3],
|
||||||
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}]
|
["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 0}],
|
||||||
|
["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0}]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
@ -1930,19 +1930,16 @@ class UserActions(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if record_card_section:
|
if record_card_section:
|
||||||
record_card_section = self.create_plain_view_section(
|
record_card_section = self._create_record_card_view_section(
|
||||||
result["id"],
|
result["id"],
|
||||||
table_id,
|
table_id,
|
||||||
self._docmodel.view_sections,
|
self._docmodel.view_sections
|
||||||
"single",
|
|
||||||
""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if primary_view or raw_section or record_card_section:
|
if primary_view or raw_section:
|
||||||
self.UpdateRecord('_grist_Tables', result["id"], {
|
self.UpdateRecord('_grist_Tables', result["id"], {
|
||||||
'primaryViewId': primary_view["id"] if primary_view else 0,
|
'primaryViewId': primary_view["id"] if primary_view else 0,
|
||||||
'rawViewSectionRef': raw_section.id if raw_section else 0,
|
'rawViewSectionRef': raw_section.id if raw_section else 0,
|
||||||
'recordCardViewSectionRef': record_card_section.id if record_card_section else 0,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -1994,10 +1991,10 @@ class UserActions(object):
|
|||||||
new_raw_section = new_table.rawViewSectionRef
|
new_raw_section = new_table.rawViewSectionRef
|
||||||
new_record_card_section = new_table.recordCardViewSectionRef
|
new_record_card_section = new_table.recordCardViewSectionRef
|
||||||
|
|
||||||
# Copy view section options to the new raw and record card view sections.
|
# Copy view section description and options to the new raw view section.
|
||||||
self._docmodel.update(
|
self._docmodel.update([new_raw_section],
|
||||||
[new_raw_section, new_record_card_section],
|
description=raw_section.description,
|
||||||
options=[raw_section.options, record_card_section.options]
|
options=raw_section.options,
|
||||||
)
|
)
|
||||||
|
|
||||||
old_to_new_col_refs = {}
|
old_to_new_col_refs = {}
|
||||||
@ -2034,6 +2031,7 @@ class UserActions(object):
|
|||||||
recalcWhen=existing_column.recalcWhen,
|
recalcWhen=existing_column.recalcWhen,
|
||||||
recalcDeps=new_recalc_deps,
|
recalcDeps=new_recalc_deps,
|
||||||
formula=formula_updates.get(new_column, existing_column.formula),
|
formula=formula_updates.get(new_column, existing_column.formula),
|
||||||
|
description=existing_column.description,
|
||||||
)
|
)
|
||||||
self.maybe_copy_display_formula(existing_column, new_column)
|
self.maybe_copy_display_formula(existing_column, new_column)
|
||||||
|
|
||||||
@ -2049,6 +2047,8 @@ class UserActions(object):
|
|||||||
for rule in existing_column.rules:
|
for rule in existing_column.rules:
|
||||||
self.doAddRule(new_table_id, None, new_column.id, rule.formula)
|
self.doAddRule(new_table_id, None, new_column.id, rule.formula)
|
||||||
|
|
||||||
|
self._copy_record_card_settings(record_card_section, new_record_card_section)
|
||||||
|
|
||||||
# Copy all row conditional styles to the new table.
|
# Copy all row conditional styles to the new table.
|
||||||
for rule in raw_section.rules:
|
for rule in raw_section.rules:
|
||||||
self.doAddRule(new_table_id, None, None, rule.formula)
|
self.doAddRule(new_table_id, None, None, rule.formula)
|
||||||
@ -2064,6 +2064,55 @@ class UserActions(object):
|
|||||||
'raw_section_id': new_raw_section.id,
|
'raw_section_id': new_raw_section.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _copy_record_card_settings(self, src_record_card_section, dst_record_card_section):
|
||||||
|
"""
|
||||||
|
Helper that copies settings from `src_record_card_section` to `dst_record_card_section`.
|
||||||
|
"""
|
||||||
|
old_to_new_col_refs = {}
|
||||||
|
old_to_new_field_refs = {}
|
||||||
|
for existing_field, new_field in zip(src_record_card_section.fields,
|
||||||
|
dst_record_card_section.fields):
|
||||||
|
old_to_new_col_refs[existing_field.colRef.id] = new_field.colRef
|
||||||
|
old_to_new_field_refs[existing_field.id] = new_field.id
|
||||||
|
|
||||||
|
for existing_field, new_field in zip(src_record_card_section.fields,
|
||||||
|
dst_record_card_section.fields):
|
||||||
|
# Copy field settings to the new fields.
|
||||||
|
self._docmodel.update(
|
||||||
|
[new_field],
|
||||||
|
displayCol=old_to_new_col_refs.get(existing_field.displayCol.id, 0),
|
||||||
|
parentPos=existing_field.parentPos,
|
||||||
|
visibleCol=old_to_new_col_refs.get(existing_field.visibleCol.id, 0),
|
||||||
|
widgetOptions=existing_field.widgetOptions,
|
||||||
|
)
|
||||||
|
|
||||||
|
if existing_field.rules:
|
||||||
|
# Copy all field conditional styles to the new section.
|
||||||
|
for rule in existing_field.rules:
|
||||||
|
self.doAddRule(dst_record_card_section.tableRef.tableId, new_field.id, None, rule.formula)
|
||||||
|
|
||||||
|
def patch_layout_spec(layout_spec):
|
||||||
|
if isinstance(layout_spec, (dict, list)):
|
||||||
|
for k, v in (layout_spec.items()
|
||||||
|
if isinstance(layout_spec, dict)
|
||||||
|
else enumerate(layout_spec)):
|
||||||
|
if k == 'leaf' and v in old_to_new_field_refs:
|
||||||
|
layout_spec[k] = old_to_new_field_refs[v]
|
||||||
|
patch_layout_spec(v)
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_layout_spec = json.loads(src_record_card_section.layoutSpec)
|
||||||
|
patch_layout_spec(new_layout_spec)
|
||||||
|
new_layout_spec = json.dumps(new_layout_spec)
|
||||||
|
except ValueError:
|
||||||
|
new_layout_spec = ''
|
||||||
|
|
||||||
|
# Copy options, theme, and layout to the new record card view section.
|
||||||
|
self._docmodel.update([dst_record_card_section],
|
||||||
|
options=src_record_card_section.options,
|
||||||
|
layoutSpec=new_layout_spec,
|
||||||
|
theme=src_record_card_section.theme,
|
||||||
|
)
|
||||||
|
|
||||||
def _fetch_table_col_recs(self, table_ref, col_refs):
|
def _fetch_table_col_recs(self, table_ref, col_refs):
|
||||||
"""Helper that converts col_refs from table table_ref into column Records."""
|
"""Helper that converts col_refs from table table_ref into column Records."""
|
||||||
@ -2123,6 +2172,15 @@ class UserActions(object):
|
|||||||
limit=limit)
|
limit=limit)
|
||||||
return section
|
return section
|
||||||
|
|
||||||
|
def _create_record_card_view_section(self, tableRef, tableId, view_sections):
|
||||||
|
section = self._docmodel.add(view_sections, tableRef=tableRef, parentKey='single',
|
||||||
|
title='', borderWidth=1, defaultWidth=100)[0]
|
||||||
|
self.UpdateRecord('_grist_Tables', tableRef, {
|
||||||
|
'recordCardViewSectionRef': section.id,
|
||||||
|
})
|
||||||
|
self._RebuildViewFields(tableId, section.id)
|
||||||
|
return section
|
||||||
|
|
||||||
@useraction
|
@useraction
|
||||||
def UpdateSummaryViewSection(self, section_ref, groupby_colrefs):
|
def UpdateSummaryViewSection(self, section_ref, groupby_colrefs):
|
||||||
"""
|
"""
|
||||||
@ -2247,14 +2305,22 @@ class UserActions(object):
|
|||||||
if section_rec.fields:
|
if section_rec.fields:
|
||||||
self._docmodel.remove(section_rec.fields)
|
self._docmodel.remove(section_rec.fields)
|
||||||
|
|
||||||
# Include all table columns that are intended to be visible to the user.
|
is_card = section_rec.parentKey in ('single', 'detail')
|
||||||
cols = [c for c in table_rec.columns if column.is_visible_column(c.colId)
|
is_record_card = section_rec == table_rec.recordCardViewSectionRef
|
||||||
# TODO: hack to avoid auto-adding the 'group' column when detaching summary tables.
|
if is_card and not is_record_card:
|
||||||
and c.colId != 'group']
|
# Copy settings from the table's record card section to the new section.
|
||||||
cols.sort(key=lambda c: c.parentPos)
|
record_card_section = table_rec.recordCardViewSectionRef
|
||||||
if limit is not None:
|
self._docmodel.add(section_rec.fields, colRef=[f.colRef for f in record_card_section.fields])
|
||||||
cols = cols[:limit]
|
self._copy_record_card_settings(record_card_section, section_rec)
|
||||||
self._docmodel.add(section_rec.fields, colRef=[c.id for c in cols])
|
else :
|
||||||
|
# Include all table columns that are intended to be visible to the user.
|
||||||
|
cols = [c for c in table_rec.columns if column.is_visible_column(c.colId)
|
||||||
|
# TODO: hack to avoid auto-adding the 'group' column when detaching summary tables.
|
||||||
|
and c.colId != 'group']
|
||||||
|
cols.sort(key=lambda c: c.parentPos)
|
||||||
|
if limit is not None:
|
||||||
|
cols = cols[:limit]
|
||||||
|
self._docmodel.add(section_rec.fields, colRef=[c.id for c in cols])
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
@ -515,8 +515,8 @@ describe('RawData', function () {
|
|||||||
await gu.checkTextEditor("abc");
|
await gu.checkTextEditor("abc");
|
||||||
await gu.sendKeys(Key.ESCAPE);
|
await gu.sendKeys(Key.ESCAPE);
|
||||||
// Click on another cell, check page hasn't changed (there was a bug about that)
|
// Click on another cell, check page hasn't changed (there was a bug about that)
|
||||||
await gu.getCell({rowNum: 10, col: 1}).click();
|
await gu.getCell({rowNum: 21, col: 1}).click();
|
||||||
assert.deepEqual(await gu.getCursorPosition(), {rowNum: 10, col: 1});
|
assert.deepEqual(await gu.getCursorPosition(), {rowNum: 21, col: 1});
|
||||||
assert.equal(await gu.getCurrentPageName(), 'City');
|
assert.equal(await gu.getCurrentPageName(), 'City');
|
||||||
|
|
||||||
// Close by hitting escape.
|
// Close by hitting escape.
|
||||||
|
@ -23,7 +23,10 @@ describe('RecordCards', function() {
|
|||||||
it('opens popup when keyboard shortcut is pressed', async function() {
|
it('opens popup when keyboard shortcut is pressed', async function() {
|
||||||
await gu.sendKeys(Key.SPACE);
|
await gu.sendKeys(Key.SPACE);
|
||||||
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
||||||
assert.equal(await driver.find('.test-widget-title-text').getText(), 'COUNTRY Card');
|
assert.equal(
|
||||||
|
await driver.find('.test-record-card-popup-wrapper .test-widget-title-text').getText(),
|
||||||
|
'COUNTRY Card'
|
||||||
|
);
|
||||||
assert.equal(await gu.getCardCell('Code').getText(), 'ALB');
|
assert.equal(await gu.getCardCell('Code').getText(), 'ALB');
|
||||||
assert.isFalse(await driver.find('.grist-single-record__menu').isPresent());
|
assert.isFalse(await driver.find('.grist-single-record__menu').isPresent());
|
||||||
await gu.sendKeys(Key.ESCAPE);
|
await gu.sendKeys(Key.ESCAPE);
|
||||||
@ -32,7 +35,10 @@ describe('RecordCards', function() {
|
|||||||
it('opens popup when menu item is clicked', async function() {
|
it('opens popup when menu item is clicked', async function() {
|
||||||
await (await gu.openRowMenu(2)).findContent('li', /View as card/).click();
|
await (await gu.openRowMenu(2)).findContent('li', /View as card/).click();
|
||||||
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
||||||
assert.equal(await driver.find('.test-widget-title-text').getText(), 'COUNTRY Card');
|
assert.equal(
|
||||||
|
await driver.find('.test-record-card-popup-wrapper .test-widget-title-text').getText(),
|
||||||
|
'COUNTRY Card'
|
||||||
|
);
|
||||||
assert.equal(await gu.getCardCell('Code').getText(), 'AND');
|
assert.equal(await gu.getCardCell('Code').getText(), 'AND');
|
||||||
await gu.sendKeys(Key.ESCAPE);
|
await gu.sendKeys(Key.ESCAPE);
|
||||||
});
|
});
|
||||||
@ -76,7 +82,10 @@ describe('RecordCards', function() {
|
|||||||
it('opens popup when reference icon is clicked', async function() {
|
it('opens popup when reference icon is clicked', async function() {
|
||||||
await gu.getCell(0, 4).find('.test-ref-link-icon').click();
|
await gu.getCell(0, 4).find('.test-ref-link-icon').click();
|
||||||
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
||||||
assert.equal(await driver.find('.test-widget-title-text').getText(), 'COUNTRY Card');
|
assert.equal(
|
||||||
|
await driver.find('.test-record-card-popup-wrapper .test-widget-title-text').getText(),
|
||||||
|
'COUNTRY Card'
|
||||||
|
);
|
||||||
assert.equal(await gu.getCardCell('Code').getText(), 'AFG');
|
assert.equal(await gu.getCardCell('Code').getText(), 'AFG');
|
||||||
assert.isFalse(await driver.find('.grist-single-record__menu').isPresent());
|
assert.isFalse(await driver.find('.grist-single-record__menu').isPresent());
|
||||||
await gu.sendKeys(Key.ESCAPE);
|
await gu.sendKeys(Key.ESCAPE);
|
||||||
@ -86,10 +95,16 @@ describe('RecordCards', function() {
|
|||||||
await gu.getCell(0, 4).find('.test-ref-text').click();
|
await gu.getCell(0, 4).find('.test-ref-text').click();
|
||||||
await gu.sendKeys(Key.SPACE);
|
await gu.sendKeys(Key.SPACE);
|
||||||
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
||||||
assert.equal(await driver.find('.test-widget-title-text').getText(), 'COUNTRYLANGUAGE Card');
|
assert.equal(
|
||||||
|
await driver.find('.test-record-card-popup-wrapper .test-widget-title-text').getText(),
|
||||||
|
'COUNTRYLANGUAGE Card'
|
||||||
|
);
|
||||||
assert.equal(await gu.getCardCell('Country').getText(), 'AFG');
|
assert.equal(await gu.getCardCell('Country').getText(), 'AFG');
|
||||||
await gu.getCardCell('Country').find('.test-ref-link-icon').click();
|
await gu.getCardCell('Country').find('.test-ref-link-icon').click();
|
||||||
assert.equal(await driver.find('.test-widget-title-text').getText(), 'COUNTRY Card');
|
assert.equal(
|
||||||
|
await driver.find('.test-record-card-popup-wrapper .test-widget-title-text').getText(),
|
||||||
|
'COUNTRY Card'
|
||||||
|
);
|
||||||
assert.equal(await gu.getCardCell('Code').getText(), 'AFG');
|
assert.equal(await gu.getCardCell('Code').getText(), 'AFG');
|
||||||
await gu.sendKeys(Key.ESCAPE);
|
await gu.sendKeys(Key.ESCAPE);
|
||||||
});
|
});
|
||||||
@ -119,7 +134,10 @@ describe('RecordCards', function() {
|
|||||||
it('opens popup when reference icon is clicked', async function() {
|
it('opens popup when reference icon is clicked', async function() {
|
||||||
await gu.getCell(0, 4).find('.test-ref-list-link-icon').click();
|
await gu.getCell(0, 4).find('.test-ref-list-link-icon').click();
|
||||||
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
||||||
assert.equal(await driver.find('.test-widget-title-text').getText(), 'COUNTRY Card');
|
assert.equal(
|
||||||
|
await driver.find('.test-record-card-popup-wrapper .test-widget-title-text').getText(),
|
||||||
|
'COUNTRY Card'
|
||||||
|
);
|
||||||
assert.equal(await gu.getCardCell('Code').getText(), 'AFG');
|
assert.equal(await gu.getCardCell('Code').getText(), 'AFG');
|
||||||
assert.isFalse(await driver.find('.grist-single-record__menu').isPresent());
|
assert.isFalse(await driver.find('.grist-single-record__menu').isPresent());
|
||||||
await gu.sendKeys(Key.ESCAPE);
|
await gu.sendKeys(Key.ESCAPE);
|
||||||
@ -129,12 +147,37 @@ describe('RecordCards', function() {
|
|||||||
await gu.getCell(0, 4).click();
|
await gu.getCell(0, 4).click();
|
||||||
await gu.sendKeys(Key.SPACE);
|
await gu.sendKeys(Key.SPACE);
|
||||||
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
assert.isTrue(await driver.findWait('.test-record-card-popup-overlay', 100).isDisplayed());
|
||||||
assert.equal(await driver.find('.test-widget-title-text').getText(), 'COUNTRYLANGUAGE Card');
|
assert.equal(
|
||||||
|
await driver.find('.test-record-card-popup-wrapper .test-widget-title-text').getText(),
|
||||||
|
'COUNTRYLANGUAGE Card'
|
||||||
|
);
|
||||||
assert.equal(await gu.getCardCell('Country').getText(), 'AFG');
|
assert.equal(await gu.getCardCell('Country').getText(), 'AFG');
|
||||||
await gu.getCardCell('Country').find('.test-ref-list-link-icon').click();
|
await gu.getCardCell('Country').find('.test-ref-list-link-icon').click();
|
||||||
assert.equal(await driver.find('.test-widget-title-text').getText(), 'COUNTRY Card');
|
assert.equal(
|
||||||
|
await driver.find('.test-record-card-popup-wrapper .test-widget-title-text').getText(),
|
||||||
|
'COUNTRY Card'
|
||||||
|
);
|
||||||
assert.equal(await gu.getCardCell('Code').getText(), 'AFG');
|
assert.equal(await gu.getCardCell('Code').getText(), 'AFG');
|
||||||
await gu.sendKeys(Key.ESCAPE);
|
await gu.sendKeys(Key.ESCAPE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('RawData', function() {
|
||||||
|
before(async function() {
|
||||||
|
await driver.find('.test-tools-raw').click();
|
||||||
|
await driver.findWait('.test-raw-data-list', 2000);
|
||||||
|
await gu.waitForServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('opens popup when reference icon is clicked', async function() {
|
||||||
|
await driver.findContent('.test-raw-data-table-title', 'City').click();
|
||||||
|
await gu.waitForServer();
|
||||||
|
await gu.getCell(1, 5).find('.test-ref-link-icon').click();
|
||||||
|
assert.equal(
|
||||||
|
await driver.find('.test-raw-data-overlay .test-widget-title-text').getText(),
|
||||||
|
'COUNTRY Card'
|
||||||
|
);
|
||||||
|
assert.equal(await gu.getCardCell('Code').getText(), 'NLD');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user