From caf830db08e3fde1be77a25d63db8da1a14bb1ac Mon Sep 17 00:00:00 2001 From: George Gevoian Date: Sun, 19 Nov 2023 19:46:32 -0500 Subject: [PATCH] (core) Record Cards Summary: Adds a new Record Card view section to each non-summary table, which can be from opened from various parts of the Grist UI to view and edit records in a popup card view. Work is still ongoing, so the feature is locked away behind a flag; follow-up work is planned to finish up the implementation and add end-to-end tests. Test Plan: Python and server tests. Browser tests will be included in a follow-up. Reviewers: jarek, paulfitz Reviewed By: jarek Subscribers: paulfitz Differential Revision: https://phab.getgrist.com/D4114 --- app/client/components/DataTables.ts | 247 +++++++++++++++---- app/client/components/DetailView.js | 23 +- app/client/components/GridView.js | 46 +++- app/client/components/GristDoc.ts | 74 ++++-- app/client/components/RawDataPage.ts | 12 +- app/client/components/RecordCardPopup.ts | 69 ++++++ app/client/components/RecordLayout.js | 6 +- app/client/components/RefSelect.ts | 2 +- app/client/components/ViewConfigTab.js | 14 +- app/client/components/ViewLayout.ts | 14 +- app/client/components/buildViewSectionDom.ts | 23 +- app/client/components/commandList.ts | 6 + app/client/models/entities/TableRec.ts | 2 + app/client/models/entities/ViewSectionRec.ts | 23 +- app/client/models/features.ts | 4 + app/client/ui/CardContextMenu.ts | 49 ++++ app/client/ui/CellContextMenu.ts | 30 ++- app/client/ui/FieldContextMenu.ts | 3 +- app/client/ui/Pages.ts | 2 +- app/client/ui/RightPanel.ts | 55 +++-- app/client/ui/RowContextMenu.ts | 22 +- app/client/ui/ShareMenu.ts | 11 +- app/client/ui/ViewLayoutMenu.ts | 9 +- app/client/ui/ViewSectionMenu.ts | 2 + app/client/ui/WidgetTitle.ts | 92 ++++++- app/client/ui2018/menus.ts | 28 ++- app/client/widgets/CellStyle.ts | 6 +- app/client/widgets/Reference.ts | 67 ++++- app/common/gristUrls.ts | 21 +- app/common/schema.ts | 4 +- app/server/lib/initialDocSql.ts | 14 +- app/server/lib/sendAppPage.ts | 1 + buildtools/fly-template.toml | 1 + sandbox/grist/docmodel.py | 3 + sandbox/grist/migrations.py | 54 ++++ sandbox/grist/schema.py | 3 +- sandbox/grist/summary.py | 3 +- sandbox/grist/test_column_actions.py | 50 ++-- sandbox/grist/test_display_cols.py | 54 ++-- sandbox/grist/test_docmodel.py | 43 ++-- sandbox/grist/test_import_actions.py | 68 ++--- sandbox/grist/test_table_actions.py | 48 ++-- sandbox/grist/test_useractions.py | 174 ++++++++----- sandbox/grist/testscript.json | 86 ++++--- sandbox/grist/useractions.py | 97 ++++++-- test/fixtures/docs/Hello.grist | Bin 61440 -> 61440 bytes test/fixtures/docs/World-v39.grist | Bin 0 -> 463872 bytes test/nbrowser/LinkingErrors.ts | 14 +- test/nbrowser/RawData.ts | 10 +- test/nbrowser/ReferenceColumns.ts | 12 +- test/nbrowser/TypeChange.ntest.js | 2 +- test/nbrowser/gristUtils.ts | 5 +- test/server/lib/DocApi.ts | 13 +- 53 files changed, 1263 insertions(+), 458 deletions(-) create mode 100644 app/client/components/RecordCardPopup.ts create mode 100644 app/client/ui/CardContextMenu.ts create mode 100644 test/fixtures/docs/World-v39.grist diff --git a/app/client/components/DataTables.ts b/app/client/components/DataTables.ts index c606464f..c75cca09 100644 --- a/app/client/components/DataTables.ts +++ b/app/client/components/DataTables.ts @@ -1,23 +1,28 @@ +import * as commands from 'app/client/components/commands'; import {GristDoc} from 'app/client/components/GristDoc'; import {copyToClipboard} from 'app/client/lib/clipboardUtils'; import {setTestState} from 'app/client/lib/testState'; import {TableRec} from 'app/client/models/DocModel'; +import {RECORD_CARDS} from 'app/client/models/features'; import {docListHeader, docMenuTrigger} from 'app/client/ui/DocMenuCss'; import {duplicateTable, DuplicateTableResponse} from 'app/client/ui/DuplicateTable'; -import {showTransientTooltip} from 'app/client/ui/tooltips'; +import {hoverTooltip, showTransientTooltip} from 'app/client/ui/tooltips'; import {buildTableName} from 'app/client/ui/WidgetTitle'; import * as css from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {loadingDots} from 'app/client/ui2018/loaders'; -import {menu, menuItem, menuText} from 'app/client/ui2018/menus'; +import {menu, menuDivider, menuIcon, menuItem, menuItemAsync, menuText} from 'app/client/ui2018/menus'; import {confirmModal} from 'app/client/ui2018/modals'; -import {Computed, Disposable, dom, fromKo, makeTestId, Observable, styled} from 'grainjs'; +import {Computed, Disposable, dom, fromKo, makeTestId, observable, Observable, styled} from 'grainjs'; import {makeT} from 'app/client/lib/localization'; +import * as weasel from 'popweasel'; const testId = makeTestId('test-raw-data-'); const t = makeT('DataTables'); +const DATA_TABLES_TOOLTIP_KEY = 'dataTablesTooltip'; + export class DataTables extends Disposable { private _tables: Observable; @@ -47,17 +52,19 @@ export class DataTables extends Disposable { testId('list'), cssHeader(t("Raw Data Tables")), cssList( - dom.forEach(this._tables, tableRec => - cssItem( + dom.forEach(this._tables, tableRec => { + const isEditingName = observable(false); + return cssTable( + dom.autoDispose(isEditingName), testId('table'), - cssLeft( + cssTableIcon( dom.domComputed((use) => cssTableTypeIcon( use(tableRec.summarySourceTable) !== 0 ? 'PivotLight' : 'TypeTable', testId(`table-id-${use(tableRec.tableId)}`) )), ), - cssMiddle( - cssTitleRow(cssTableTitle(this._tableTitle(tableRec), testId('table-title'))), + cssTableNameAndId( + cssTitleRow(cssTableTitle(this._tableTitle(tableRec, isEditingName), testId('table-title'))), cssDetailsRow( cssTableIdWrapper(cssHoverWrapper( cssUpperCase("Table ID: "), @@ -76,14 +83,34 @@ export class DataTables extends Disposable { setTestState({clipboard: tableRec.tableId.peek()}); }) )), - this._tableRows(tableRec), ), ), - cssRight( - docMenuTrigger( + this._tableRows(tableRec), + cssTableButtons( + cssRecordCardButton( + icon('TypeCard'), + dom.on('click', (ev) => { + ev.stopPropagation(); + ev.preventDefault(); + if (!tableRec.recordCardViewSection().disabled()) { + this._editRecordCard(tableRec); + } + }), + hoverTooltip( + dom.domComputed(use => use(use(tableRec.recordCardViewSection).disabled) + ? t('Record Card Disabled') + : t('Record Card')), + {key: DATA_TABLES_TOOLTIP_KEY, closeOnClick: false} + ), + dom.hide(!RECORD_CARDS()), + // Make the button invisible to maintain consistent alignment with non-summary tables. + dom.style('visibility', u => u(tableRec.summarySourceTable) === 0 ? 'visible' : 'hidden'), + cssRecordCardButton.cls('-disabled', use => use(use(tableRec.recordCardViewSection).disabled)), + ), + cssDotsButton( testId('table-menu'), icon('Dots'), - menu(() => this._menuItems(tableRec), {placement: 'bottom-start'}), + menu(() => this._menuItems(tableRec, isEditingName), {placement: 'bottom-start'}), dom.on('click', (ev) => { ev.stopPropagation(); ev.preventDefault(); }), ) ), @@ -94,14 +121,14 @@ export class DataTables extends Disposable { } this._gristDoc.viewModel.activeSectionId(sectionId); }) - ) - ) + ); + }) ), ), ); } - private _tableTitle(table: TableRec) { + private _tableTitle(table: TableRec, isEditing: Observable) { return dom.domComputed((use) => { const rawViewSectionRef = use(fromKo(table.rawViewSectionRef)); const isSummaryTable = use(table.summarySourceTable) !== 0; @@ -113,37 +140,75 @@ export class DataTables extends Disposable { ].filter(p => Boolean(p?.trim())).join(' '); return cssTableName(tableName); } else { - return dom('div', // to disable flex grow in the widget + return cssFlexRow( dom.domComputed(fromKo(table.rawViewSection), vs => - buildTableName(vs, testId('widget-title')) - ) + buildTableName(vs, {isEditing}, cssRenamableTableName.cls(''), testId('widget-title')) + ), + cssRenameTableButton(icon('Pencil'), + dom.on('click', (ev) => { + ev.stopPropagation(); + ev.preventDefault(); + isEditing.set(true); + }), + cssRenameTableButton.cls('-active', isEditing), + ), ); } }); } - private _menuItems(table: TableRec) { + private _menuItems(table: TableRec, isEditingName: Observable) { const {isReadonly, docModel} = this._gristDoc; return [ + menuItem( + () => { isEditingName.set(true); }, + t("Rename Table"), + dom.cls('disabled', use => use(isReadonly) || use(table.summarySourceTable) !== 0), + testId('menu-rename-table'), + ), menuItem( () => this._duplicateTable(table), t("Duplicate Table"), - testId('menu-duplicate-table'), dom.cls('disabled', use => use(isReadonly) || use(table.isHidden) || use(table.summarySourceTable) !== 0 ), + testId('menu-duplicate-table'), ), menuItem( () => this._removeTable(table), - 'Remove', - testId('menu-remove'), + t("Remove Table"), dom.cls('disabled', use => use(isReadonly) || ( // Can't delete last visible table, unless it is a hidden table. use(docModel.visibleTables.getObservable()).length <= 1 && !use(table.isHidden) - )) + )), + testId('menu-remove-table'), ), + dom.maybe(use => RECORD_CARDS() && use(table.summarySourceTable) === 0, () => [ + menuDivider(), + menuItem( + () => this._editRecordCard(table), + cssMenuItemIcon('TypeCard'), + t("Edit Record Card"), + dom.cls('disabled', use => use(isReadonly)), + testId('menu-edit-record-card'), + ), + dom.domComputed(use => use(use(table.recordCardViewSection).disabled), (isDisabled) => { + return menuItemAsync( + async () => { + if (isDisabled) { + await this._enableRecordCard(table); + } else { + await this._disableRecordCard(table); + } + }, + t('{{action}} Record Card', {action: isDisabled ? 'Enable' : 'Disable'}), + dom.cls('disabled', use => use(isReadonly)), + testId(`menu-${isDisabled ? 'enable' : 'disable'}-record-card`), + ); + }), + ]), dom.maybe(isReadonly, () => menuText(t("You do not have edit access to this document"))), ]; } @@ -166,6 +231,24 @@ export class DataTables extends Disposable { ), 'Delete', doRemove); } + private _editRecordCard(r: TableRec) { + const sectionId = r.recordCardViewSection.peek().getRowId(); + if (!sectionId) { + throw new Error(`Table ${r.tableId.peek()} doesn't have a record card view section.`); + } + + this._gristDoc.viewModel.activeSectionId(sectionId); + commands.allCommands.editLayout.run(); + } + + private async _enableRecordCard(r: TableRec) { + await r.recordCardViewSection().disabled.setAndSave(false); + } + + private async _disableRecordCard(r: TableRec) { + await r.recordCardViewSection().disabled.setAndSave(true); + } + private _tableRows(table: TableRec) { return dom.maybe(this._rowCount, (rowCounts) => { if (rowCounts === 'hidden') { return null; } @@ -183,6 +266,18 @@ export class DataTables extends Disposable { } } +const cssMenuItemIcon = styled(menuIcon, ` + --icon-color: ${css.theme.menuItemFg}; + + .${weasel.cssMenuItem.className}-sel & { + --icon-color: ${css.theme.menuItemSelectedFg}; + } + + .${weasel.cssMenuItem.className}.disabled & { + --icon-color: ${css.theme.menuItemDisabledFg}; + } +`); + const container = styled('div', ` overflow-y: auto; position: relative; @@ -198,42 +293,37 @@ const cssList = styled('div', ` gap: 12px; `); -const cssItem = styled('div', ` - display: flex; - align-items: center; +const cssTable = styled('div', ` + display: grid; + grid-template-columns: 16px auto 100px 56px; + grid-template-rows: 1fr; + grid-column-gap: 8px; cursor: pointer; border-radius: 3px; width: 100%; height: calc(1em * 56/13); /* 56px for 13px font */ max-width: 750px; + padding: 0px 12px 0px 12px; border: 1px solid ${css.theme.rawDataTableBorder}; &:hover { border-color: ${css.theme.rawDataTableBorderHover}; } `); -// Holds icon in top left corner -const cssLeft = styled('div', ` +const cssTableIcon = styled('div', ` padding-top: 11px; - padding-left: 12px; - margin-right: 8px; - align-self: flex-start; display: flex; - flex: none; `); -const cssMiddle = styled('div', ` - flex-grow: 1; +const cssTableNameAndId = styled('div', ` min-width: 0px; display: flex; - flex-wrap: wrap; - margin-top: 6px; - margin-bottom: 4px; + flex-direction: column; + margin-top: 8px; `); const cssTitleRow = styled('div', ` min-width: 100%; - margin-right: 4px; `); const cssDetailsRow = styled('div', ` @@ -243,13 +333,12 @@ const cssDetailsRow = styled('div', ` `); -// Holds dots menu (which is 24px x 24px, but has its own 4px right margin) -const cssRight = styled('div', ` - padding-right: 8px; - margin-left: 8px; - align-self: center; +// Holds dots menu (which is 24px x 24px) +const cssTableButtons = styled('div', ` display: flex; - flex: none; + align-items: center; + justify-content: flex-end; + column-gap: 8px; `); const cssTableTypeIcon = styled(icon, ` @@ -270,13 +359,10 @@ const cssTableIdWrapper = styled('div', ` const cssTableRowsWrapper = styled('div', ` display: flex; - flex-shrink: 0; - min-width: 100px; overflow: hidden; - align-items: baseline; + align-items: center; color: ${css.theme.lightText}; line-height: 18px; - padding: 0px 2px; `); const cssHoverWrapper = styled('div', ` @@ -301,6 +387,8 @@ const cssTableRows = cssTableId; const cssTableTitle = styled('div', ` color: ${css.theme.text}; + overflow: hidden; + text-overflow: ellipsis; white-space: nowrap; `); @@ -327,3 +415,66 @@ const cssLoadingDots = styled(loadingDots, ` const cssTableName = styled('span', ` color: ${css.theme.text}; `); + +const cssRecordCardButton = styled('div', ` + display: flex; + align-items: center; + justify-content: center; + height: 24px; + width: 24px; + cursor: default; + padding: 4px; + border-radius: 3px; + --icon-color: ${css.theme.lightText}; + + &:hover { + background-color: ${css.theme.hover}; + --icon-color: ${css.theme.controlFg}; + } + + &-disabled { + --icon-color: ${css.theme.lightText}; + padding: 0px; + opacity: 0.4; + } + + &-disabled:hover { + background: none; + --icon-color: ${css.theme.lightText}; + } +`); + +const cssDotsButton = styled(docMenuTrigger, ` + margin: 0px; + + &:hover, &.weasel-popup-open { + background-color: ${css.theme.hover}; + } +`); + +const cssRenameTableButton = styled('div', ` + flex-shrink: 0; + width: 16px; + visibility: hidden; + cursor: default; + --icon-color: ${css.theme.lightText}; + &:hover { + --icon-color: ${css.theme.controlFg}; + } + &-active { + visibility: hidden; + } + .${cssTableTitle.className}:hover & { + visibility: visible; + } +`); + +const cssFlexRow = styled('div', ` + display: flex; + align-items: center; + column-gap: 8px; +`); + +const cssRenamableTableName = styled('div', ` + flex: initial; +`); diff --git a/app/client/components/DetailView.js b/app/client/components/DetailView.js index 99058ea4..9e7e4f7a 100644 --- a/app/client/components/DetailView.js +++ b/app/client/components/DetailView.js @@ -17,8 +17,8 @@ const {CopySelection} = require('./CopySelection'); const RecordLayout = require('./RecordLayout'); const commands = require('./commands'); const tableUtil = require('../lib/tableUtil'); +const {CardContextMenu} = require('../ui/CardContextMenu'); const {FieldContextMenu} = require('../ui/FieldContextMenu'); -const {RowContextMenu} = require('../ui/RowContextMenu'); const {parsePasteForView} = require("./BaseView2"); const {descriptionInfoTooltip} = require("../ui/tooltips"); @@ -39,7 +39,7 @@ function DetailView(gristDoc, viewSectionModel) { this.recordLayout = this.autoDispose(RecordLayout.create({ viewSection: this.viewSection, buildFieldDom: this.buildFieldDom.bind(this), - buildRowContextMenu : this.buildRowContextMenu.bind(this), + buildCardContextMenu : this.buildCardContextMenu.bind(this), buildFieldContextMenu : this.buildFieldContextMenu.bind(this), resizeCallback: () => { if (!this._isSingle) { @@ -246,15 +246,14 @@ DetailView.prototype.getSelection = function() { ); }; -DetailView.prototype.buildRowContextMenu = function(row) { - const rowOptions = this._getRowContextMenuOptions(row); - return RowContextMenu(rowOptions); +DetailView.prototype.buildCardContextMenu = function(row) { + const cardOptions = this._getCardContextMenuOptions(row); + return CardContextMenu(cardOptions); } -DetailView.prototype.buildFieldContextMenu = function(row) { - const rowOptions = this._getRowContextMenuOptions(row); +DetailView.prototype.buildFieldContextMenu = function() { const fieldOptions = this._getFieldContextMenuOptions(); - return FieldContextMenu(rowOptions, fieldOptions); + return FieldContextMenu(fieldOptions); } /** @@ -490,8 +489,9 @@ DetailView.prototype._canSingleClick = function(field) { }; DetailView.prototype._clearCardFields = function() { - const {isFormula} = this._getFieldContextMenuOptions(); - if (isFormula === true) { + const selection = this.getSelection(); + const isFormula = Boolean(selection.fields[0]?.column.peek().isRealFormula.peek()); + if (isFormula) { this.activateEditorAtCursor({init: ''}); } else { const clearAction = tableUtil.makeDeleteAction(this.getSelection()); @@ -520,7 +520,7 @@ DetailView.prototype._clearCopySelection = function() { this.copySelection(null); }; -DetailView.prototype._getRowContextMenuOptions = function(row) { +DetailView.prototype._getCardContextMenuOptions = function(row) { return { disableInsert: Boolean( this.gristDoc.isReadonly.get() || @@ -542,7 +542,6 @@ DetailView.prototype._getFieldContextMenuOptions = function() { return { disableModify: Boolean(selection.fields[0]?.disableModify.peek()), isReadonly: this.gristDoc.isReadonly.get() || this.isPreview, - isFormula: Boolean(selection.fields[0]?.column.peek().isRealFormula.peek()), }; } diff --git a/app/client/components/GridView.js b/app/client/components/GridView.js index 13b5262c..e32cf73e 100644 --- a/app/client/components/GridView.js +++ b/app/client/components/GridView.js @@ -55,6 +55,9 @@ const {NEW_FILTER_JSON} = require('app/client/models/ColumnFilter'); const {CombinedStyle} = require("app/client/models/Styles"); const {buildRenameColumn} = require('app/client/ui/ColumnTitle'); const {makeT} = require('app/client/lib/localization'); +const {reportError} = require('app/client/models/AppModel'); +const {RECORD_CARDS} = require('app/client/models/features'); +const {urlState} = require('app/client/models/gristUrlState'); const t = makeT('GridView'); @@ -370,7 +373,17 @@ GridView.gridCommands = { return; } this.viewSection.rawNumFrozen.setAndSave(action.numFrozen); - } + }, + viewAsCard() { + if (!RECORD_CARDS()) { return; } + if (this._isRecordCardDisabled()) { return; } + + const selectedRows = this.selectedRows(); + const rowId = selectedRows[0]; + const sectionId = this.viewSection.tableRecordCard().id(); + const anchorUrlState = {hash: {rowId, sectionId, recordCard: true}}; + urlState().pushUrl(anchorUrlState, {replace: true}).catch(reportError); + }, }; GridView.prototype.onTableLoaded = function() { @@ -1909,20 +1922,41 @@ GridView.prototype.rowContextMenu = function() { GridView.prototype._getRowContextMenuOptions = function() { return { - disableInsert: Boolean(this.gristDoc.isReadonly.get() || this.viewSection.disableAddRemoveRows() || this.tableModel.tableMetaRow.onDemand()), - disableDelete: Boolean(this.gristDoc.isReadonly.get() || this.viewSection.disableAddRemoveRows() || this.getSelection().onlyAddRowSelected()), - isViewSorted: this.viewSection.activeSortSpec.peek().length > 0, - numRows: this.getSelection().rowIds.length, + ...this._getCellContextMenuOptions(), + disableShowRecordCard: this._isRecordCardDisabled(), }; }; +GridView.prototype._isRecordCardDisabled = function() { + return this.getSelection().onlyAddRowSelected() || + this.viewSection.isTableRecordCardDisabled() || + this.viewSection.table().summarySourceTable() !== 0; +} + GridView.prototype.cellContextMenu = function() { return CellContextMenu( - this._getRowContextMenuOptions(), + this._getCellContextMenuOptions(), this._getColumnMenuOptions(this.getSelection()) ); }; +GridView.prototype._getCellContextMenuOptions = function() { + return { + disableInsert: Boolean( + this.gristDoc.isReadonly.get() || + this.viewSection.disableAddRemoveRows() || + this.tableModel.tableMetaRow.onDemand() + ), + disableDelete: Boolean( + this.gristDoc.isReadonly.get() || + this.viewSection.disableAddRemoveRows() || + this.getSelection().onlyAddRowSelected() + ), + isViewSorted: this.viewSection.activeSortSpec.peek().length > 0, + numRows: this.getSelection().rowIds.length, + }; +}; + // End Context Menus GridView.prototype.scrollToCursor = function(sync = true) { diff --git a/app/client/components/GristDoc.ts b/app/client/components/GristDoc.ts index 1026014b..f2fc59cc 100644 --- a/app/client/components/GristDoc.ts +++ b/app/client/components/GristDoc.ts @@ -17,6 +17,7 @@ import {EditorMonitor} from "app/client/components/EditorMonitor"; import GridView from 'app/client/components/GridView'; import {importFromFile, selectAndImport} from 'app/client/components/Importer'; import {RawDataPage, RawDataPopup} from 'app/client/components/RawDataPage'; +import {RecordCardPopup} from 'app/client/components/RecordCardPopup'; import {ActionGroupWithCursorPos, UndoStack} from 'app/client/components/UndoStack'; import {ViewLayout} from 'app/client/components/ViewLayout'; import {get as getBrowserGlobals} from 'app/client/lib/browserGlobals'; @@ -125,7 +126,7 @@ export interface IExtraTool { content: TabContent[] | IDomComponent; } -interface RawSectionOptions { +interface PopupSectionOptions { viewSection: ViewSectionRec; hash: HashLink; close: () => void; @@ -179,7 +180,7 @@ export class GristDoc extends DisposableWithEvents { // the space. public maximizedSectionId: Observable = Observable.create(this, null); // This is id of the section that is currently shown in the popup. Probably this is an external - // section, like raw data view, or a section from another view.. + // section, like raw data view, or a section from another view. public externalSectionId: Computed; public viewLayout: ViewLayout | null = null; @@ -201,15 +202,14 @@ export class GristDoc extends DisposableWithEvents { private _rightPanelTool = createSessionObs(this, "rightPanelTool", "none", RightPanelTool.guard); private _showGristTour = getUserOrgPrefObs(this.userOrgPrefs, 'showGristTour'); private _seenDocTours = getUserOrgPrefObs(this.userOrgPrefs, 'seenDocTours'); - private _rawSectionOptions: Observable = Observable.create(this, null); - private _activeContent: Computed; + private _popupSectionOptions: Observable = Observable.create(this, null); + private _activeContent: Computed; private _docTutorialHolder = Holder.create(this); private _isRickRowing: Observable = Observable.create(this, false); private _showBackgroundVideoPlayer: Observable = Observable.create(this, false); private _backgroundVideoPlayerHolder: Holder = Holder.create(this); private _disableAutoStartingTours: boolean = false; - constructor( public readonly app: App, public readonly appModel: AppModel, @@ -256,9 +256,9 @@ export class GristDoc extends DisposableWithEvents { const viewId = this.docModel.views.tableData.findRow(docPage === 'GristDocTour' ? 'name' : 'id', docPage); return viewId || use(defaultViewId); }); - this._activeContent = Computed.create(this, use => use(this._rawSectionOptions) ?? use(this.activeViewId)); + this._activeContent = Computed.create(this, use => use(this._popupSectionOptions) ?? use(this.activeViewId)); this.externalSectionId = Computed.create(this, use => { - const externalContent = use(this._rawSectionOptions); + const externalContent = use(this._popupSectionOptions); return externalContent ? use(externalContent.viewSection.id) : null; }); // This viewModel reflects the currently active view, relying on the fact that @@ -302,7 +302,7 @@ export class GristDoc extends DisposableWithEvents { try { - if (state.hash.popup) { + if (state.hash.popup || state.hash.recordCard) { await this.openPopup(state.hash); } else { // Navigate to an anchor if one is present in the url hash. @@ -615,7 +615,17 @@ export class GristDoc extends DisposableWithEvents { owner.autoDispose(this.activeViewId.addListener(content.close)); // In case the section is removed, close the popup. content.viewSection.autoDispose({dispose: content.close}); - return dom.create(RawDataPopup, this, content.viewSection, content.close); + + const {recordCard} = content.hash; + if (recordCard) { + return dom.create(RecordCardPopup, { + gristDoc: this, + viewSection: content.viewSection, + onClose: content.close, + }); + } else { + return dom.create(RawDataPopup, this, content.viewSection, content.close); + } }) : dom.create((owner) => { this.viewLayout = ViewLayout.create(owner, this, content); @@ -671,7 +681,11 @@ export class GristDoc extends DisposableWithEvents { return; } // If this is completely unknown section (without a parent), it is probably an import preview. - if (!desiredSection.parentId.peek() && !desiredSection.isRaw.peek()) { + if ( + !desiredSection.parentId.peek() && + !desiredSection.isRaw.peek() && + !desiredSection.isRecordCard.peek() + ) { const view = desiredSection.viewInstance.peek(); // Make sure we have a view instance here - it will prove our assumption that this is // an import preview. Section might also be disconnected during undo/redo. @@ -1215,7 +1229,8 @@ export class GristDoc extends DisposableWithEvents { }, false, silent, visitedSections.concat([section.id.peek()])); } const view: ViewRec = section.view.peek(); - const docPage: ViewDocPage = section.isRaw.peek() ? "data" : view.getRowId(); + const isRawOrRecordCardView = section.isRaw.peek() || section.isRecordCard.peek(); + const docPage: ViewDocPage = isRawOrRecordCardView ? 'data' : view.getRowId(); if (docPage != this.activeViewId.get()) { await this.openDocPage(docPage); } @@ -1303,20 +1318,21 @@ export class GristDoc extends DisposableWithEvents { // We need to make it active, so that cursor on this section will be the // active one. This will change activeViewSectionId on a parent view of this section, // which might be a diffrent view from what we currently have. If the section is - // a raw data section it will use `EmptyRowModel` as raw sections don't have parents. + // a raw data or record card section, it will use `EmptyRowModel` as these sections + // don't currently have parent views. popupSection.hasFocus(true); - this._rawSectionOptions.set({ + this._popupSectionOptions.set({ hash, viewSection: popupSection, close: () => { - // In case we are already close, do nothing. - if (!this._rawSectionOptions.get()) { + // In case we are already closed, do nothing. + if (!this._popupSectionOptions.get()) { return; } if (popupSection !== prevSection) { - // We need to blur raw view section. Otherwise it will automatically be opened - // on raw data view. Note: raw data section doesn't have its own view, it uses - // empty row model as a parent (which feels like a hack). + // We need to blur the popup section. Otherwise it will automatically be opened + // on raw data view. Note: raw data and record card sections don't have parent views; + // they use the empty row model as a parent (which feels like a hack). if (!popupSection.isDisposed()) { popupSection.hasFocus(false); } @@ -1328,17 +1344,21 @@ export class GristDoc extends DisposableWithEvents { prevSection.hasFocus(true); } } - // Clearing popup data will close this popup. - this._rawSectionOptions.set(null); + // Clearing popup section data will close this popup. + this._popupSectionOptions.set(null); } }); // If the anchor link is valid, set the cursor. - if (hash.colRef && hash.rowId) { - const fieldIndex = popupSection.viewFields.peek().all().findIndex(f => f.colRef.peek() === hash.colRef); - if (fieldIndex >= 0) { - const view = await this._waitForView(popupSection); - view?.setCursorPos({rowId: hash.rowId, fieldIndex}); + if (hash.rowId || hash.colRef) { + const {rowId} = hash; + let fieldIndex; + if (hash.colRef) { + const maybeFieldIndex = popupSection.viewFields.peek().all() + .findIndex(f => f.colRef.peek() === hash.colRef); + if (maybeFieldIndex !== -1) { fieldIndex = maybeFieldIndex; } } + const view = await this._waitForView(popupSection); + view?.setCursorPos({rowId, fieldIndex}); } } @@ -1586,8 +1606,8 @@ export class GristDoc extends DisposableWithEvents { */ private async _switchToSectionId(sectionId: number) { const section: ViewSectionRec = this.docModel.viewSections.getRowModel(sectionId); - if (section.isRaw.peek()) { - // This is raw data view + if (section.isRaw.peek() || section.isRecordCard.peek()) { + // This is a raw data or record card view. await urlState().pushUrl({docPage: 'data'}); this.viewModel.activeSectionId(sectionId); } else if (section.isVirtual.peek()) { diff --git a/app/client/components/RawDataPage.ts b/app/client/components/RawDataPage.ts index e4e05e43..703ed75b 100644 --- a/app/client/components/RawDataPage.ts +++ b/app/client/components/RawDataPage.ts @@ -23,7 +23,7 @@ export class RawDataPage extends Disposable { this.autoDispose(commands.createGroup(commandGroup, this, true)); this._lightboxVisible = Computed.create(this, use => { const section = use(this._gristDoc.viewModel.activeSection); - return Boolean(use(section.id)) && use(section.isRaw); + return Boolean(use(section.id)) && (use(section.isRaw) || use(section.isRecordCard)); }); // When we are disposed, we want to clear active section in the viewModel we got (which is an empty model) // to not restore the section when user will come back to Raw Data page. @@ -55,7 +55,7 @@ export class RawDataPage extends Disposable { /*************** Lightbox section **********/ dom.domComputed(fromKo(this._gristDoc.viewModel.activeSection), (viewSection) => { const sectionId = viewSection.getRowId(); - if (!sectionId || !viewSection.isRaw.peek()) { + if (!sectionId || (!viewSection.isRaw.peek() && !viewSection.isRecordCard.peek())) { return null; } return dom.create(RawDataPopup, this._gristDoc, viewSection, () => this._close()); @@ -97,7 +97,9 @@ export class RawDataPopup extends Disposable { sectionRowId: this._viewSection.getRowId(), draggable: false, focusable: false, - widgetNameHidden: this._viewSection.isRaw.peek(), // We are sometimes used for non raw sections. + // Expanded, non-raw widgets are also rendered in RawDataPopup. + widgetNameHidden: this._viewSection.isRaw.peek(), + renamable: !this._viewSection.isRecordCard.peek(), }) ), cssCloseButton('CrossBig', @@ -127,7 +129,7 @@ const cssPage = styled('div', ` } `); -const cssOverlay = styled('div', ` +export const cssOverlay = styled('div', ` background-color: ${theme.modalBackdrop}; inset: 0px; height: 100%; @@ -162,7 +164,7 @@ const cssSectionWrapper = styled('div', ` } `); -const cssCloseButton = styled(icon, ` +export const cssCloseButton = styled(icon, ` position: absolute; top: 16px; right: 16px; diff --git a/app/client/components/RecordCardPopup.ts b/app/client/components/RecordCardPopup.ts new file mode 100644 index 00000000..bdfe9967 --- /dev/null +++ b/app/client/components/RecordCardPopup.ts @@ -0,0 +1,69 @@ +import {buildViewSectionDom} from 'app/client/components/buildViewSectionDom'; +import * as commands from 'app/client/components/commands'; +import {GristDoc} from 'app/client/components/GristDoc'; +import {cssCloseButton, cssOverlay} from 'app/client/components/RawDataPage'; +import {ViewSectionRec} from 'app/client/models/DocModel'; +import {ViewSectionHelper} from 'app/client/components/ViewLayout'; +import {theme} from 'app/client/ui2018/cssVars'; +import {Disposable, dom, makeTestId, styled} from 'grainjs'; + +const testId = makeTestId('test-record-card-popup-'); + +interface RecordCardPopupOptions { + gristDoc: GristDoc; + viewSection: ViewSectionRec; + onClose(): void; +} + +export class RecordCardPopup extends Disposable { + private _gristDoc = this._options.gristDoc; + private _viewSection = this._options.viewSection; + private _handleClose = this._options.onClose; + + constructor(private _options: RecordCardPopupOptions) { + super(); + const commandGroup = { + cancel: () => { this._handleClose(); }, + }; + this.autoDispose(commands.createGroup(commandGroup, this, true)); + } + + public buildDom() { + ViewSectionHelper.create(this, this._gristDoc, this._viewSection); + return cssOverlay( + testId('overlay'), + cssSectionWrapper( + buildViewSectionDom({ + gristDoc: this._gristDoc, + sectionRowId: this._viewSection.getRowId(), + draggable: false, + focusable: false, + renamable: false, + hideTitleControls: true, + }), + ), + cssCloseButton('CrossBig', + dom.on('click', () => this._handleClose()), + testId('close'), + ), + dom.on('click', (ev, elem) => void (ev.target === elem ? this._handleClose() : null)), + ); + } +} + +const cssSectionWrapper = styled('div', ` + background: ${theme.mainPanelBg}; + height: 100%; + display: flex; + flex-direction: column; + border-radius: 5px; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + & .viewsection_content { + margin: 0px; + margin-top: 8px; + } + & .viewsection_title { + padding: 0px 12px; + } +`); diff --git a/app/client/components/RecordLayout.js b/app/client/components/RecordLayout.js index 17941327..ad087353 100644 --- a/app/client/components/RecordLayout.js +++ b/app/client/components/RecordLayout.js @@ -54,7 +54,7 @@ const t = makeT('RecordLayout'); function RecordLayout(options) { this.viewSection = options.viewSection; this.buildFieldDom = options.buildFieldDom; - this.buildRowContextMenu = options.buildRowContextMenu; + this.buildCardContextMenu = options.buildCardContextMenu; this.buildFieldContextMenu = options.buildFieldContextMenu; this.isEditingLayout = ko.observable(false); this.editIndex = ko.observable(0); @@ -342,7 +342,7 @@ RecordLayout.prototype.buildLayoutDom = function(row, optCreateEditor) { this.layoutEditor(null); }) : null, // enables field context menu anywhere on the card - contextMenu(() => this.buildFieldContextMenu(row)), + contextMenu(() => this.buildFieldContextMenu()), dom('div.detail_row_num', kd.text(() => (row._index() + 1)), dom.on('contextmenu', ev => { @@ -358,7 +358,7 @@ RecordLayout.prototype.buildLayoutDom = function(row, optCreateEditor) { this.viewSection.hasFocus(true); commands.allCommands.setCursor.run(row); }), - menu(() => this.buildRowContextMenu(row)), + menu(() => this.buildCardContextMenu(row)), testId('card-menu-trigger') ) ), diff --git a/app/client/components/RefSelect.ts b/app/client/components/RefSelect.ts index dade783a..08af4e8b 100644 --- a/app/client/components/RefSelect.ts +++ b/app/client/components/RefSelect.ts @@ -168,7 +168,7 @@ export class RefSelect extends Disposable { this._getReferrerFields(item.value).forEach(refField => { const sectionId = this._fieldObs()!.viewSection().getRowId(); if (refField.column().viewFields().all() - .filter(field => !field.viewSection().isRaw()) + .filter(field => !field.viewSection().isRaw() && !field.viewSection().isRecordCard()) .some(field => field.parentId() !== sectionId)) { // The col has fields in other sections, remove only the fields in this section. return this._docModel.viewFields.sendTableAction(['RemoveRecord', refField.getRowId()]); diff --git a/app/client/components/ViewConfigTab.js b/app/client/components/ViewConfigTab.js index 409e05bd..2aac898a 100644 --- a/app/client/components/ViewConfigTab.js +++ b/app/client/components/ViewConfigTab.js @@ -8,7 +8,7 @@ var koArray = require('../lib/koArray'); var commands = require('./commands'); var {CustomSectionElement} = require('../lib/CustomSectionElement'); const {ChartConfig} = require('./ChartView'); -const {Computed, dom: grainjsDom, makeTestId} = require('grainjs'); +const {Computed, dom: grainjsDom, makeTestId, Holder} = require('grainjs'); const {cssRow} = require('app/client/ui/RightPanelStyles'); const {SortFilterConfig} = require('app/client/ui/SortFilterConfig'); @@ -37,6 +37,7 @@ function ViewConfigTab(options) { var self = this; this.gristDoc = options.gristDoc; this.viewModel = options.viewModel; + this._viewSectionDataHolder = Holder.create(this); // viewModel may point to different views, but viewSectionData is a single koArray reflecting // the sections of the current view. @@ -58,18 +59,21 @@ function ViewConfigTab(options) { return this.viewModel.activeSection().parentKey() === 'custom';}, this)); this.isRaw = this.autoDispose(ko.computed(function() { return this.viewModel.activeSection().isRaw();}, this)); + this.isRecordCard = this.autoDispose(ko.computed(function() { + return this.viewModel.activeSection().isRecordCard();}, this)); - this.activeRawSectionData = this.autoDispose(ko.computed(function() { - return self.isRaw() ? ViewSectionData.create(self.viewModel.activeSection()) : null; + this.activeRawOrRecordCardSectionData = this.autoDispose(ko.computed(function() { + return self.isRaw() || self.isRecordCard() + ? self._viewSectionDataHolder.autoDispose(ViewSectionData.create(self.viewModel.activeSection())) + : null; })); - this.activeSectionData = this.autoDispose(ko.computed(function() { return ( _.find(self.viewSectionData.all(), function(sectionData) { return sectionData.section && sectionData.section.getRowId() === self.viewModel.activeSectionId(); }) - || self.activeRawSectionData() + || self.activeRawOrRecordCardSectionData() || self.viewSectionData.at(0) ); })); diff --git a/app/client/components/ViewLayout.ts b/app/client/components/ViewLayout.ts index 75b24453..b1ed218e 100644 --- a/app/client/components/ViewLayout.ts +++ b/app/client/components/ViewLayout.ts @@ -205,14 +205,14 @@ export class ViewLayout extends DisposableWithEvents implements IDomComponent { this._onResize(); // Reset active section to the first one if the section is popup is collapsed. if (prev - && this.viewModel.activeCollapsedSections.peek().includes(prev) - && this.previousSectionId) { - // Make sure that previous section exists still. - if (this.viewModel.viewSections.peek().all() - .some(s => !s.isDisposed() && s.id.peek() === this.previousSectionId)) { - this.viewModel.activeSectionId(this.previousSectionId); - } + && this.viewModel.activeCollapsedSections.peek().includes(prev) + && this.previousSectionId) { + // Make sure that previous section exists still. + if (this.viewModel.viewSections.peek().all() + .some(s => !s.isDisposed() && s.id.peek() === this.previousSectionId)) { + this.viewModel.activeSectionId(this.previousSectionId); } + } } else { // Otherwise resize only active one (the one in popup). const section = this.viewModel.activeSection.peek(); diff --git a/app/client/components/buildViewSectionDom.ts b/app/client/components/buildViewSectionDom.ts index e498e108..fc45cf7d 100644 --- a/app/client/components/buildViewSectionDom.ts +++ b/app/client/components/buildViewSectionDom.ts @@ -65,9 +65,21 @@ export function buildViewSectionDom(options: { focusable?: boolean, /* defaults to true */ tableNameHidden?: boolean, widgetNameHidden?: boolean, + renamable?: boolean, + hideTitleControls?: boolean, }) { const isResizing = options.isResizing ?? Observable.create(null, false); - const {gristDoc, sectionRowId, viewModel, draggable = true, focusable = true} = options; + const { + gristDoc, + sectionRowId, + viewModel, + draggable = true, + focusable = true, + tableNameHidden, + widgetNameHidden, + renamable = true, + hideTitleControls = false, + } = options; // Creating normal section dom const vs: ViewSectionRec = gristDoc.docModel.viewSections.getRowModel(sectionRowId); @@ -92,8 +104,13 @@ export function buildViewSectionDom(options: { ), dom.maybe((use) => use(use(viewInstance.viewSection.table).summarySourceTable), () => cssSigmaIcon('Pivot', testId('sigma'))), - buildWidgetTitle(vs, options, testId('viewsection-title'), cssTestClick(testId("viewsection-blank"))), - viewInstance.buildTitleControls(), + buildWidgetTitle( + vs, + {tableNameHidden, widgetNameHidden, disabled: !renamable}, + testId('viewsection-title'), + cssTestClick(testId("viewsection-blank")), + ), + hideTitleControls ? null : viewInstance.buildTitleControls(), dom('div.viewsection_buttons', dom.create(viewSectionMenu, gristDoc, vs) ) diff --git a/app/client/components/commandList.ts b/app/client/components/commandList.ts index 54a972df..08f2bb0f 100644 --- a/app/client/components/commandList.ts +++ b/app/client/components/commandList.ts @@ -114,6 +114,7 @@ export type CommandName = | 'clearCopySelection' | 'detachEditor' | 'activateAssistant' + | 'viewAsCard' ; @@ -270,6 +271,11 @@ export const groups: CommendGroupDef[] = [{ keys: [], desc: 'Activate assistant', }, + { + name: 'viewAsCard', + keys: [], + desc: 'Show the record card widget of the selected record', + }, ] }, { group: 'Navigation', diff --git a/app/client/models/entities/TableRec.ts b/app/client/models/entities/TableRec.ts index c247b64f..4cb2297b 100644 --- a/app/client/models/entities/TableRec.ts +++ b/app/client/models/entities/TableRec.ts @@ -15,6 +15,7 @@ export interface TableRec extends IRowModel<"_grist_Tables"> { primaryView: ko.Computed; rawViewSection: ko.Computed; + recordCardViewSection: ko.Computed; summarySource: ko.Computed; // A Set object of colRefs for all summarySourceCols of table. @@ -52,6 +53,7 @@ export function createTableRec(this: TableRec, docModel: DocModel): void { this.primaryView = refRecord(docModel.views, this.primaryViewId); this.rawViewSection = refRecord(docModel.viewSections, this.rawViewSectionRef); + this.recordCardViewSection = refRecord(docModel.viewSections, this.recordCardViewSectionRef); this.summarySource = refRecord(docModel.tables, this.summarySourceTable); this.isHidden = this.autoDispose( // This is repeated logic from isHiddenTable. diff --git a/app/client/models/entities/ViewSectionRec.ts b/app/client/models/entities/ViewSectionRec.ts index 56791faf..bc170587 100644 --- a/app/client/models/entities/ViewSectionRec.ts +++ b/app/client/models/entities/ViewSectionRec.ts @@ -85,6 +85,19 @@ export interface ViewSectionRec extends IRowModel<"_grist_Views_section">, RuleO // true if this record is its table's rawViewSection, i.e. a 'raw data view' // in which case the UI prevents various things like hiding columns or changing the widget type. isRaw: ko.Computed; + + tableRecordCard: ko.Computed + isRecordCard: ko.Computed; + + /** True if this section is disabled. Currently only used by Record Card sections. */ + disabled: modelUtil.KoSaveableObservable; + + /** + * True if the Record Card section of this section's table is disabled. Shortcut for + * `this.tableRecordCard().disabled()`. + */ + isTableRecordCardDisabled: ko.Computed; + isVirtual: ko.Computed; isCollapsed: ko.Computed; @@ -443,7 +456,13 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): // true if this record is its table's rawViewSection, i.e. a 'raw data view' // in which case the UI prevents various things like hiding columns or changing the widget type. - this.isRaw = this.autoDispose(ko.pureComputed(() => this.table().rawViewSectionRef() === this.getRowId())); + this.isRaw = this.autoDispose(ko.pureComputed(() => this.table().rawViewSectionRef() === this.id())); + + this.tableRecordCard = this.autoDispose(ko.pureComputed(() => this.table().recordCardViewSection())); + this.isRecordCard = this.autoDispose(ko.pureComputed(() => + this.table().recordCardViewSectionRef() === this.id())); + this.disabled = modelUtil.fieldWithDefault(this.optionsObj.prop('disabled'), false); + this.isTableRecordCardDisabled = ko.pureComputed(() => this.tableRecordCard().disabled()); this.isVirtual = this.autoDispose(ko.pureComputed(() => typeof this.id() === 'string')); @@ -818,7 +837,7 @@ export function createViewSectionRec(this: ViewSectionRec, docModel: DocModel): let newColInfo: NewColInfo; await docModel.docData.bundleActions('Insert column', async () => { newColInfo = await docModel.dataTables[this.tableId.peek()].sendTableAction(action); - if (!this.isRaw.peek()) { + if (!this.isRaw.peek() && !this.isRecordCard.peek()) { const fieldInfo = { colRef: newColInfo.colRef, parentId: this.id.peek(), diff --git a/app/client/models/features.ts b/app/client/models/features.ts index 353fe0a3..a1bcc03b 100644 --- a/app/client/models/features.ts +++ b/app/client/models/features.ts @@ -33,3 +33,7 @@ export function PERMITTED_CUSTOM_WIDGETS(): Observable { } return G.window.PERMITTED_CUSTOM_WIDGETS; } + +export function RECORD_CARDS() { + return Boolean(getGristConfig().experimentalPlugins); +} diff --git a/app/client/ui/CardContextMenu.ts b/app/client/ui/CardContextMenu.ts new file mode 100644 index 00000000..f5ad6fd7 --- /dev/null +++ b/app/client/ui/CardContextMenu.ts @@ -0,0 +1,49 @@ +import { allCommands } from 'app/client/components/commands'; +import { makeT } from 'app/client/lib/localization'; +import { menuDivider, menuItemCmd } from 'app/client/ui2018/menus'; +import { dom } from 'grainjs'; + +const t = makeT('CardContextMenu'); + +export interface ICardContextMenu { + disableInsert: boolean; + disableDelete: boolean; + isViewSorted: boolean; + numRows: number; +} + +export function CardContextMenu({ + disableInsert, + disableDelete, + isViewSorted, + numRows +}: ICardContextMenu) { + const result: Element[] = []; + if (isViewSorted) { + result.push( + menuItemCmd(allCommands.insertRecordAfter, t("Insert card"), + dom.cls('disabled', disableInsert)), + ); + } else { + result.push( + menuItemCmd(allCommands.insertRecordBefore, t("Insert card above"), + dom.cls('disabled', disableInsert)), + menuItemCmd(allCommands.insertRecordAfter, t("Insert card below"), + dom.cls('disabled', disableInsert)), + ); + } + result.push( + menuItemCmd(allCommands.duplicateRows, t("Duplicate card"), + dom.cls('disabled', disableInsert || numRows === 0)), + ); + result.push( + menuDivider(), + menuItemCmd(allCommands.deleteRecords, t("Delete card"), + dom.cls('disabled', disableDelete)), + ); + result.push( + menuDivider(), + menuItemCmd(allCommands.copyLink, t("Copy anchor link")) + ); + return result; +} diff --git a/app/client/ui/CellContextMenu.ts b/app/client/ui/CellContextMenu.ts index 7952cd82..9bb13702 100644 --- a/app/client/ui/CellContextMenu.ts +++ b/app/client/ui/CellContextMenu.ts @@ -2,32 +2,36 @@ import { allCommands } from 'app/client/components/commands'; import { makeT } from 'app/client/lib/localization'; import { menuDivider, menuItemCmd } from 'app/client/ui2018/menus'; import { IMultiColumnContextMenu } from 'app/client/ui/GridViewMenus'; -import { IRowContextMenu } from 'app/client/ui/RowContextMenu'; import { COMMENTS } from 'app/client/models/features'; import { dom } from 'grainjs'; const t = makeT('CellContextMenu'); -export function CellContextMenu(rowOptions: IRowContextMenu, colOptions: IMultiColumnContextMenu) { +export interface ICellContextMenu { + disableInsert: boolean; + disableDelete: boolean; + isViewSorted: boolean; + numRows: number; +} + +export function CellContextMenu(cellOptions: ICellContextMenu, colOptions: IMultiColumnContextMenu) { - const { disableInsert, disableDelete, isViewSorted } = rowOptions; - const { disableModify, isReadonly } = colOptions; + const { disableInsert, disableDelete, isViewSorted, numRows } = cellOptions; + const { numColumns, disableModify, isReadonly, isFiltered } = colOptions; // disableModify is true if the column is a summary column or is being transformed. // isReadonly is true for readonly mode. const disableForReadonlyColumn = dom.cls('disabled', Boolean(disableModify) || isReadonly); const disableForReadonlyView = dom.cls('disabled', isReadonly); - const numCols: number = colOptions.numColumns; - const nameClearColumns = colOptions.isFiltered ? - t("Reset {{count}} entire columns", {count: numCols}) : - t("Reset {{count}} columns", {count: numCols}); - const nameDeleteColumns = t("Delete {{count}} columns", {count: numCols}); + const nameClearColumns = isFiltered ? + t("Reset {{count}} entire columns", {count: numColumns}) : + t("Reset {{count}} columns", {count: numColumns}); + const nameDeleteColumns = t("Delete {{count}} columns", {count: numColumns}); - const numRows: number = rowOptions.numRows; const nameDeleteRows = t("Delete {{count}} rows", {count: numRows}); - const nameClearCells = (numRows > 1 || numCols > 1) ? t("Clear values") : t("Clear cell"); + const nameClearCells = (numRows > 1 || numColumns > 1) ? t("Clear values") : t("Clear cell"); const result: Array = []; @@ -42,13 +46,13 @@ export function CellContextMenu(rowOptions: IRowContextMenu, colOptions: IMultiC menuItemCmd(allCommands.clearColumns, nameClearColumns, disableForReadonlyColumn), ...( - (numCols > 1 || numRows > 1) ? [] : [ + (numColumns > 1 || numRows > 1) ? [] : [ menuDivider(), menuItemCmd(allCommands.copyLink, t("Copy anchor link")), menuDivider(), menuItemCmd(allCommands.filterByThisCellValue, t("Filter by this value")), menuItemCmd(allCommands.openDiscussion, t('Comment'), dom.cls('disabled', ( - isReadonly || numRows === 0 || numCols === 0 + isReadonly || numRows === 0 || numColumns === 0 )), dom.hide(use => !use(COMMENTS()))) //TODO: i18next ] ), diff --git a/app/client/ui/FieldContextMenu.ts b/app/client/ui/FieldContextMenu.ts index 68b3735f..ddeeccd1 100644 --- a/app/client/ui/FieldContextMenu.ts +++ b/app/client/ui/FieldContextMenu.ts @@ -1,6 +1,5 @@ import {allCommands} from 'app/client/components/commands'; import {makeT} from 'app/client/lib/localization'; -import {IRowContextMenu} from 'app/client/ui/RowContextMenu'; import {menuDivider, menuItemCmd} from 'app/client/ui2018/menus'; import {dom} from 'grainjs'; @@ -11,7 +10,7 @@ export interface IFieldContextMenu { isReadonly: boolean; } -export function FieldContextMenu(_rowOptions: IRowContextMenu, fieldOptions: IFieldContextMenu) { +export function FieldContextMenu(fieldOptions: IFieldContextMenu) { const {disableModify, isReadonly} = fieldOptions; const disableForReadonlyColumn = dom.cls('disabled', disableModify || isReadonly); return [ diff --git a/app/client/ui/Pages.ts b/app/client/ui/Pages.ts index 3d13fd7a..47ccfd42 100644 --- a/app/client/ui/Pages.ts +++ b/app/client/ui/Pages.ts @@ -86,7 +86,7 @@ function removeView(activeDoc: GristDoc, viewId: number, pageName: string) { const docData = activeDoc.docData; // Create a set with tables on other pages (but not on this one). const tablesOnOtherViews = new Set(activeDoc.docModel.viewSections.rowModels - .filter(vs => !vs.isRaw.peek() && vs.parentId.peek() !== viewId) + .filter(vs => !vs.isRaw.peek() && !vs.isRecordCard.peek() && vs.parentId.peek() !== viewId) .map(vs => vs.tableRef.peek())); // Check if this page is a last page for some tables. diff --git a/app/client/ui/RightPanel.ts b/app/client/ui/RightPanel.ts index 27232392..da91ffb4 100644 --- a/app/client/ui/RightPanel.ts +++ b/app/client/ui/RightPanel.ts @@ -356,7 +356,10 @@ export class RightPanel extends Disposable { dom.maybe(this._validSection, (activeSection) => ( buildConfigContainer( subTab === 'widget' ? dom.create(this._buildPageWidgetConfig.bind(this), activeSection) : - subTab === 'sortAndFilter' ? dom.create(this._buildPageSortFilterConfig.bind(this)) : + subTab === 'sortAndFilter' ? [ + dom.create(this._buildPageSortFilterConfig.bind(this)), + cssConfigContainer.cls('-disabled', activeSection.isRecordCard), + ] : subTab === 'data' ? dom.create(this._buildPageDataConfig.bind(this), activeSection) : null ) @@ -397,33 +400,35 @@ export class RightPanel extends Disposable { return dom.maybe(viewConfigTab, (vct) => [ this._disableIfReadonly(), - cssLabel(dom.text(use => use(activeSection.isRaw) ? t("DATA TABLE NAME") : t("WIDGET TITLE")), - dom.style('margin-bottom', '14px'), - ), - cssRow(cssTextInput( - Computed.create(owner, (use) => use(activeSection.titleDef)), - val => activeSection.titleDef.saveOnly(val), - dom.boolAttr('disabled', use => { - const isRawTable = use(activeSection.isRaw); - const isSummaryTable = use(use(activeSection.table).summarySourceTable) !== 0; - return isRawTable && isSummaryTable; - }), - testId('right-widget-title') - )), - - cssSection( - dom.create(buildDescriptionConfig, activeSection.description, { cursor, "testPrefix": "right-widget" }), - ), + dom.maybe(use => !use(activeSection.isRecordCard), () => [ + cssLabel(dom.text(use => use(activeSection.isRaw) ? t("DATA TABLE NAME") : t("WIDGET TITLE")), + dom.style('margin-bottom', '14px'), + ), + cssRow(cssTextInput( + Computed.create(owner, (use) => use(activeSection.titleDef)), + val => activeSection.titleDef.saveOnly(val), + dom.boolAttr('disabled', use => { + const isRawTable = use(activeSection.isRaw); + const isSummaryTable = use(use(activeSection.table).summarySourceTable) !== 0; + return isRawTable && isSummaryTable; + }), + testId('right-widget-title') + )), + + cssSection( + dom.create(buildDescriptionConfig, activeSection.description, { cursor, "testPrefix": "right-widget" }), + ), + ]), dom.maybe( - (use) => !use(activeSection.isRaw), + (use) => !use(activeSection.isRaw) && !use(activeSection.isRecordCard), () => cssRow( primaryButton(t("Change Widget"), this._createPageWidgetPicker()), cssRow.cls('-top-space') ), ), - cssSeparator(), + cssSeparator(dom.hide(activeSection.isRecordCard)), dom.maybe((use) => ['detail', 'single'].includes(use(this._pageWidgetType)!), () => [ cssLabel(t("Theme")), @@ -744,7 +749,7 @@ export class RightPanel extends Disposable { dom.hide((use) => !use(use(table).summarySourceTable)), ), - dom.maybe((use) => !use(activeSection.isRaw), () => + dom.maybe((use) => !use(activeSection.isRaw) && !use(activeSection.isRecordCard), () => cssButtonRow(primaryButton(t("Edit Data Selection"), this._createPageWidgetPicker(), testId('pwc-editDataSelection')), dom.maybe( @@ -764,9 +769,9 @@ export class RightPanel extends Disposable { dom.maybe(viewConfigTab, (vct) => cssRow( dom('div', vct._buildAdvancedSettingsDom()), )), - cssSeparator(), - dom.maybe((use) => !use(activeSection.isRaw), () => [ + dom.maybe((use) => !use(activeSection.isRaw) && !use(activeSection.isRecordCard), () => [ + cssSeparator(), cssLabel(t("SELECT BY")), cssRow( dom.update( @@ -1033,6 +1038,10 @@ const cssConfigContainer = styled('div.test-config-container', ` & .fieldbuilder_settings { margin: 16px 0 0 0; } + &-disabled { + opacity: 0.4; + pointer-events: none; + } `); const cssDataLabel = styled('div', ` diff --git a/app/client/ui/RowContextMenu.ts b/app/client/ui/RowContextMenu.ts index f05cb255..87b75e17 100644 --- a/app/client/ui/RowContextMenu.ts +++ b/app/client/ui/RowContextMenu.ts @@ -1,6 +1,7 @@ import { allCommands } from 'app/client/components/commands'; import { makeT } from 'app/client/lib/localization'; -import { menuDivider, menuItemCmd } from 'app/client/ui2018/menus'; +import { RECORD_CARDS } from 'app/client/models/features'; +import { menuDivider, menuIcon, menuItemCmd, menuItemCmdLabel } from 'app/client/ui2018/menus'; import { dom } from 'grainjs'; const t = makeT('RowContextMenu'); @@ -8,12 +9,29 @@ const t = makeT('RowContextMenu'); export interface IRowContextMenu { disableInsert: boolean; disableDelete: boolean; + disableShowRecordCard: boolean; isViewSorted: boolean; numRows: number; } -export function RowContextMenu({ disableInsert, disableDelete, isViewSorted, numRows }: IRowContextMenu) { +export function RowContextMenu({ + disableInsert, + disableDelete, + disableShowRecordCard, + isViewSorted, + numRows +}: IRowContextMenu) { const result: Element[] = []; + if (RECORD_CARDS() && numRows === 1) { + result.push( + menuItemCmd( + allCommands.viewAsCard, + () => menuItemCmdLabel(menuIcon('TypeCard'), t("View as card")), + dom.cls('disabled', disableShowRecordCard), + ), + menuDivider(), + ); + } if (isViewSorted) { // When the view is sorted, any newly added records get shifts instantly at the top or // bottom. It could be very confusing for users who might expect the record to stay above or diff --git a/app/client/ui/ShareMenu.ts b/app/client/ui/ShareMenu.ts index 1e922c0a..7386523f 100644 --- a/app/client/ui/ShareMenu.ts +++ b/app/client/ui/ShareMenu.ts @@ -16,7 +16,7 @@ import {buildUrlId, isFeatureEnabled, parseUrlId} from 'app/common/gristUrls'; import * as roles from 'app/common/roles'; import {Document} from 'app/common/UserAPI'; import {dom, DomContents, styled} from 'grainjs'; -import {MenuCreateFunc} from 'popweasel'; +import {cssMenuItem, MenuCreateFunc} from 'popweasel'; import {makeT} from 'app/client/lib/localization'; const t = makeT('ShareMenu'); @@ -378,9 +378,12 @@ const cssMenuIconLink = styled('a', ` padding: 8px 24px; --icon-color: ${theme.controlFg}; - &:hover { - background-color: ${theme.hover}; - --icon-color: ${theme.controlHoverFg}; + .${cssMenuItem.className}-sel > & { + --icon-color: ${theme.menuItemIconSelectedFg}; + } + + .${cssMenuItem.className}.disabled & { + --icon-color: ${theme.menuItemDisabledFg}; } `); diff --git a/app/client/ui/ViewLayoutMenu.ts b/app/client/ui/ViewLayoutMenu.ts index 8a39ad7f..a42f1c13 100644 --- a/app/client/ui/ViewLayoutMenu.ts +++ b/app/client/ui/ViewLayoutMenu.ts @@ -58,6 +58,7 @@ export function makeViewLayoutMenu(viewSection: ViewSectionRec, isReadonly: bool const showRawData = (use: UseCB) => { return !use(viewSection.isRaw)// Don't show raw data if we're already in raw data. + && !use(viewSection.isRecordCard) && !isSinglePage // Don't show raw data in single page mode. ; }; @@ -88,20 +89,22 @@ export function makeViewLayoutMenu(viewSection: ViewSectionRec, isReadonly: bool dom.maybe(!isSinglePage, () => [ menuDivider(), menuItemCmd(allCommands.viewTabOpen, t("Widget options"), testId('widget-options')), - menuItemCmd(allCommands.sortFilterTabOpen, t("Advanced Sort & Filter")), - menuItemCmd(allCommands.dataSelectionTabOpen, t("Data selection")), + menuItemCmd(allCommands.sortFilterTabOpen, t("Advanced Sort & Filter"), dom.hide(viewSection.isRecordCard)), + menuItemCmd(allCommands.dataSelectionTabOpen, t("Data selection"), dom.hide(viewSection.isRecordCard)), ]), - menuDivider(), + menuDivider(dom.hide(viewSection.isRecordCard)), dom.maybe((use) => use(viewSection.parentKey) === 'custom' && use(viewSection.hasCustomOptions), () => menuItemCmd(allCommands.openWidgetConfiguration, t("Open configuration"), testId('section-open-configuration')), ), menuItemCmd(allCommands.collapseSection, t("Collapse widget"), dom.cls('disabled', dontCollapseSection()), + dom.hide(viewSection.isRecordCard), testId('section-collapse')), menuItemCmd(allCommands.deleteSection, t("Delete widget"), dom.cls('disabled', dontRemoveSection()), + dom.hide(viewSection.isRecordCard), testId('section-delete')), ]; } diff --git a/app/client/ui/ViewSectionMenu.ts b/app/client/ui/ViewSectionMenu.ts index e7241167..6329eb87 100644 --- a/app/client/ui/ViewSectionMenu.ts +++ b/app/client/ui/ViewSectionMenu.ts @@ -69,6 +69,7 @@ export function viewSectionMenu( && use(gristDoc.maximizedSectionId) !== use(viewSection.id) // not in when we are maximized && use(gristDoc.externalSectionId) !== use(viewSection.id) // not in when we are external && !use(viewSection.isRaw) // not in raw mode + && !use(viewSection.isRecordCard) && !use(singleVisible) // not in single section ; }); @@ -145,6 +146,7 @@ export function viewSectionMenu( ctl.close(); }), ]}), + dom.hide(viewSection.isRecordCard), ), cssMenu( testId('viewLayout'), diff --git a/app/client/ui/WidgetTitle.ts b/app/client/ui/WidgetTitle.ts index fb13bf3f..bb08d16f 100644 --- a/app/client/ui/WidgetTitle.ts +++ b/app/client/ui/WidgetTitle.ts @@ -7,7 +7,7 @@ import { theme } from 'app/client/ui2018/cssVars'; import {menuCssClass} from 'app/client/ui2018/menus'; import {ModalControl} from 'app/client/ui2018/modals'; import { Computed, dom, DomElementArg, makeTestId, Observable, styled } from 'grainjs'; -import {IOpenController, setPopupToCreateDom} from 'popweasel'; +import {IOpenController, IPopupOptions, PopupControl, setPopupToCreateDom} from 'popweasel'; import { descriptionInfoTooltip } from './tooltips'; import { autoGrow } from './forms'; import { cssInput, cssLabel, cssRenamePopup, cssTextArea } from 'app/client/ui/RenamePopupStyles'; @@ -18,41 +18,105 @@ const t = makeT('WidgetTitle'); interface WidgetTitleOptions { tableNameHidden?: boolean, widgetNameHidden?: boolean, + disabled?: boolean, } export function buildWidgetTitle(vs: ViewSectionRec, options: WidgetTitleOptions, ...args: DomElementArg[]) { const title = Computed.create(null, use => use(vs.titleDef)); const description = Computed.create(null, use => use(vs.description)); - return buildRenameWidget(vs, title, description, options, dom.autoDispose(title), ...args); + return buildRenamableTitle(vs, title, description, options, dom.autoDispose(title), ...args); } -export function buildTableName(vs: ViewSectionRec, ...args: DomElementArg[]) { +interface TableNameOptions { + isEditing: Observable, + disabled?: boolean, +} + +export function buildTableName(vs: ViewSectionRec, options: TableNameOptions, ...args: DomElementArg[]) { const title = Computed.create(null, use => use(use(vs.table).tableNameDef)); const description = Computed.create(null, use => use(vs.description)); - return buildRenameWidget(vs, title, description, { widgetNameHidden: true }, dom.autoDispose(title), ...args); + return buildRenamableTitle( + vs, + title, + description, + { + openOnClick: false, + widgetNameHidden: true, + ...options, + }, + dom.autoDispose(title), + ...args + ); } -export function buildRenameWidget( +interface RenamableTitleOptions { + tableNameHidden?: boolean, + widgetNameHidden?: boolean, + /** Defaults to true. */ + openOnClick?: boolean, + isEditing?: Observable, + disabled?: boolean, +} + +function buildRenamableTitle( vs: ViewSectionRec, title: Observable, description: Observable, - options: WidgetTitleOptions, - ...args: DomElementArg[]) { + options: RenamableTitleOptions, + ...args: DomElementArg[] +) { + const {openOnClick = true, disabled = false, isEditing, ...renameTitleOptions} = options; + let popupControl: PopupControl | undefined; return cssTitleContainer( cssTitle( testId('text'), dom.text(title), + dom.on('click', () => { + // The popup doesn't close if `openOnClick` is false and the title is + // clicked. Make sure that it does. + if (!openOnClick) { popupControl?.close(); } + }), // In case titleDef is all blank space, make it visible on hover. cssTitle.cls("-empty", use => !use(title)?.trim()), + cssTitle.cls("-open-on-click", openOnClick), + cssTitle.cls("-disabled", disabled), elem => { - setPopupToCreateDom(elem, ctl => buildWidgetRenamePopup(ctl, vs, options), { + if (disabled) { return; } + + // The widget title popup can be configured to open in up to two ways: + // 1. When the title is clicked - done by setting `openOnClick` to `true`. + // 2. When `isEditing` is set to true - done by setting `isEditing` to `true`. + // + // Typically, the former should be set. The latter is useful for triggering the + // popup from a different part of the UI, like a menu item. + const trigger: IPopupOptions['trigger'] = []; + if (openOnClick) { trigger.push('click'); } + if (isEditing) { + trigger.push((_: Element, ctl: PopupControl) => { + popupControl = ctl; + ctl.autoDispose(isEditing.addListener((editing) => { + if (editing) { + ctl.open(); + } else if (!ctl.isDisposed()) { + ctl.close(); + } + })); + }); + } + setPopupToCreateDom(elem, ctl => { + if (isEditing) { + ctl.onDispose(() => isEditing.set(false)); + } + + return buildRenameTitlePopup(ctl, vs, renameTitleOptions); + }, { placement: 'bottom-start', - trigger: ['click'], + trigger, attach: 'body', boundaries: 'viewport', }); }, - dom.on('click', (ev) => { ev.stopPropagation(); ev.preventDefault(); }), + openOnClick ? dom.on('click', (ev) => { ev.stopPropagation(); ev.preventDefault(); }) : null, ), dom.maybe(description, () => [ descriptionInfoTooltip(description.get(), "widget") @@ -61,7 +125,7 @@ export function buildRenameWidget( ); } -function buildWidgetRenamePopup(ctrl: IOpenController, vs: ViewSectionRec, options: WidgetTitleOptions) { +function buildRenameTitlePopup(ctrl: IOpenController, vs: ViewSectionRec, options: RenamableTitleOptions) { const tableRec = vs.table.peek(); // If the table is a summary table. const isSummary = Boolean(tableRec.summarySourceTable.peek()); @@ -279,14 +343,16 @@ const cssTitleContainer = styled('div', ` `); const cssTitle = styled('div', ` - cursor: pointer; overflow: hidden; border-radius: 3px; margin: -4px; padding: 4px; text-overflow: ellipsis; align-self: start; - &:hover { + &-open-on-click:not(&-disabled) { + cursor: pointer; + } + &-open-on-click:not(&-disabled):hover { background-color: ${theme.hover}; } &-empty { diff --git a/app/client/ui2018/menus.ts b/app/client/ui2018/menus.ts index 9650ce13..ba2e508c 100644 --- a/app/client/ui2018/menus.ts +++ b/app/client/ui2018/menus.ts @@ -9,7 +9,7 @@ import { IconName } from 'app/client/ui2018/IconList'; import { icon } from 'app/client/ui2018/icons'; import { cssSelectBtn } from 'app/client/ui2018/select'; import { - BindableValue, Computed, dom, DomElementArg, DomElementMethod, IDomArgs, + BindableValue, Computed, dom, DomContents, DomElementArg, DomElementMethod, IDomArgs, MaybeObsArray, MutableObsArray, Observable, styled } from 'grainjs'; import debounce from 'lodash/debounce'; @@ -574,16 +574,27 @@ export const menuItemAsync: typeof weasel.menuItem = function(action, ...args) { return menuItem(() => setTimeout(action, 0), ...args); }; -export function menuItemCmd(cmd: Command, label: string, ...args: DomElementArg[]) { +export function menuItemCmd( + cmd: Command, + label: string | (() => DomContents), + ...args: DomElementArg[] +) { return menuItem( () => cmd.run(), - dom('span', label, testId('cmd-name')), + typeof label === 'string' + ? dom('span', label, testId('cmd-name')) + : dom('div', label(), testId('cmd-name')), cmd.humanKeys.length ? cssCmdKey(cmd.humanKeys[0]) : null, cssMenuItemCmd.cls(''), // overrides some menu item styles ...args ); } +export const menuItemCmdLabel = styled('div', ` + display: flex; + align-items: center; +`); + export function menuAnnotate(text: string, ...args: DomElementArg[]) { return cssAnnotateMenuItem(text, ...args); } @@ -701,6 +712,15 @@ const cssInputButtonMenuElem = styled(cssMenuElem, ` const cssMenuItemCmd = styled('div', ` justify-content: space-between; + --icon-color: ${theme.menuItemFg}; + + .${weasel.cssMenuItem.className}-sel & { + --icon-color: ${theme.menuItemSelectedFg}; + } + + .${weasel.cssMenuItem.className}.disabled & { + --icon-color: ${theme.menuItemDisabledFg}; + } `); const cssCmdKey = styled('span', ` @@ -712,7 +732,7 @@ const cssCmdKey = styled('span', ` color: ${theme.menuItemIconSelectedFg}; } - .${weasel.cssMenuItem.className}.disabled > & { + .${weasel.cssMenuItem.className}.disabled & { color: ${theme.menuItemDisabledFg}; } `); diff --git a/app/client/widgets/CellStyle.ts b/app/client/widgets/CellStyle.ts index a7192baa..6699dd4d 100644 --- a/app/client/widgets/CellStyle.ts +++ b/app/client/widgets/CellStyle.ts @@ -77,7 +77,11 @@ export class CellStyle extends Disposable { }), cssLine( cssLabel(t('CELL STYLE')), - cssButton(t('Open row styles'), dom.on('click', allCommands.viewTabOpen.run)), + cssButton( + t('Open row styles'), + dom.on('click', allCommands.viewTabOpen.run), + dom.hide(!isTableWidget), + ), ), cssRow( testId('cell-color-select'), diff --git a/app/client/widgets/Reference.ts b/app/client/widgets/Reference.ts index f060222e..296b39a7 100644 --- a/app/client/widgets/Reference.ts +++ b/app/client/widgets/Reference.ts @@ -1,12 +1,16 @@ import {makeT} from 'app/client/lib/localization'; import {DataRowModel} from 'app/client/models/DataRowModel'; +import {TableRec} from 'app/client/models/DocModel'; import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec'; +import {RECORD_CARDS} from 'app/client/models/features'; +import {urlState} from 'app/client/models/gristUrlState'; import {cssLabel, cssRow} from 'app/client/ui/RightPanelStyles'; import {hideInPrintView, testId, theme} from 'app/client/ui2018/cssVars'; import {icon} from 'app/client/ui2018/icons'; import {IOptionFull, select} from 'app/client/ui2018/menus'; import {NTextBox} from 'app/client/widgets/NTextBox'; import {isFullReferencingType, isVersions} from 'app/common/gristTypes'; +import {UIRowId} from 'app/plugin/GristAPI'; import {Computed, dom, styled} from 'grainjs'; @@ -16,6 +20,7 @@ const t = makeT('Reference'); * Reference - The widget for displaying references to another table's records. */ export class Reference extends NTextBox { + private _refTable: Computed; private _visibleColRef: Computed; private _validCols: Computed>>; @@ -26,8 +31,10 @@ export class Reference extends NTextBox { // Note that saveOnly is used here to prevent display value flickering on visible col change. this._visibleColRef.onWrite((val) => this.field.visibleColRef.saveOnly(val)); + this._refTable = Computed.create(this, (use) => use(use(this.field.column).refTable)); + this._validCols = Computed.create(this, (use) => { - const refTable = use(use(this.field.column).refTable); + const refTable = use(this._refTable); if (!refTable) { return []; } return use(use(refTable.columns).getObservable()) .filter(col => !use(col.isHiddenCol)) @@ -75,16 +82,16 @@ export class Reference extends NTextBox { return id && use(id); }); const formattedValue = Computed.create(null, (use) => { - let [value, hasBlankReference] = ['', false]; + let [value, hasBlankReference, hasRecordCard] = ['', false, false]; if (use(row._isAddRow) || this.isDisposed() || use(this.field.displayColModel).isDisposed()) { // Work around JS errors during certain changes (noticed when visibleCol field gets removed // for a column using per-field settings). - return {value, hasBlankReference}; + return {value, hasBlankReference, hasRecordCard}; } const displayValueObs = row.cells[use(use(this.field.displayColModel).colId)]; if (!displayValueObs) { - return {value, hasBlankReference}; + return {value, hasBlankReference, hasRecordCard}; } const displayValue = use(displayValueObs); @@ -97,8 +104,12 @@ export class Reference extends NTextBox { use(this.field.formatter).formatAny(displayValue); hasBlankReference = referenceId.get() !== 0 && value.trim() === ''; + const refTable = use(this._refTable); + if (refTable) { + hasRecordCard = !use(use(refTable.recordCardViewSection).disabled); + } - return {value, hasBlankReference}; + return {value, hasBlankReference, hasRecordCard}; }); return cssRef( @@ -107,12 +118,39 @@ export class Reference extends NTextBox { cssRef.cls('-blank', use => use(formattedValue).hasBlankReference), dom.style('text-align', this.alignment), dom.cls('text_wrapping', this.wrapping), - cssRefIcon('FieldReference', testId('ref-link-icon'), hideInPrintView()), - dom.text(use => { - if (use(referenceId) === 0) { return ''; } - if (use(formattedValue).hasBlankReference) { return '[Blank]'; } - return use(formattedValue).value; - }) + cssRefIcon('FieldReference', + cssRefIcon.cls('-view-as-card', use => + RECORD_CARDS() && use(referenceId) !== 0 && use(formattedValue).hasRecordCard), + dom.on('click', async () => { + if (!RECORD_CARDS()) { return; } + if (referenceId.get() === 0 || !formattedValue.get().hasRecordCard) { return; } + + const rowId = referenceId.get() as UIRowId; + const sectionId = this._refTable.get()?.recordCardViewSectionRef(); + if (sectionId === undefined) { + throw new Error('Unable to find Record Card section'); + } + + const anchorUrlState = {hash: {rowId, sectionId, recordCard: true}}; + await urlState().pushUrl(anchorUrlState, {replace: true}); + }), + dom.on('mousedown', (ev) => { + if (!RECORD_CARDS()) { return; } + + ev.stopPropagation(); + ev.preventDefault(); + }), + hideInPrintView(), + testId('ref-link-icon'), + ), + dom('span', + dom.text(use => { + if (use(referenceId) === 0) { return ''; } + if (use(formattedValue).hasBlankReference) { return '[Blank]'; } + return use(formattedValue).value; + }), + testId('ref-text'), + ), ); } } @@ -121,6 +159,13 @@ const cssRefIcon = styled(icon, ` float: left; --icon-color: ${theme.lightText}; margin: -1px 2px 2px 0; + + &-view-as-card { + cursor: pointer; + } + &-view-as-card:hover { + --icon-color: ${theme.controlFg}; + } `); const cssRef = styled('div.field_clip', ` diff --git a/app/common/gristUrls.ts b/app/common/gristUrls.ts index dc2a5a27..cbc479ed 100644 --- a/app/common/gristUrls.ts +++ b/app/common/gristUrls.ts @@ -290,9 +290,15 @@ export function encodeUrl(gristConfig: Partial, queryParams[`${k}_`] = v; } const hashParts: string[] = []; - if (state.hash && (state.hash.rowId || state.hash.popup)) { + if (state.hash && (state.hash.rowId || state.hash.popup || state.hash.recordCard)) { const hash = state.hash; - hashParts.push(state.hash?.popup ? 'a2' : `a1`); + if (hash.recordCard) { + hashParts.push('a3'); + } else if (hash.popup) { + hashParts.push('a2'); + } else { + hashParts.push('a1'); + } for (const key of ['sectionId', 'rowId', 'colRef'] as Array) { const partValue = hash[key]; if (partValue) { @@ -480,13 +486,13 @@ export function decodeUrl(gristConfig: Partial, location: Locat hashMap.set(part.slice(0, 1), part.slice(1)); } } - if (hashMap.has('#') && ['a1', 'a2'].includes(hashMap.get('#') || '')) { + if (hashMap.has('#') && ['a1', 'a2', 'a3'].includes(hashMap.get('#') || '')) { const link: HashLink = {}; const keys = [ 'sectionId', 'rowId', 'colRef', - ] as Array>; + ] as Array>; for (const key of keys) { let ch: string; if (key === 'rowId' && hashMap.has('rr')) { @@ -504,7 +510,9 @@ export function decodeUrl(gristConfig: Partial, location: Locat } } if (hashMap.get('#') === 'a2') { - link.popup = true; + link.popup = true; + } else if (hashMap.get('#') === 'a3') { + link.recordCard = true; } state.hash = link; } @@ -722,6 +730,8 @@ export interface GristLoadConfig { // Whether to show the "Delete Account" button in the account page. canCloseAccount?: boolean; + + experimentalPlugins?: boolean; } export const Features = StringUnion( @@ -966,6 +976,7 @@ export interface HashLink { colRef?: number; popup?: boolean; rickRow?: boolean; + recordCard?: boolean; } // Check whether a urlId is a prefix of the docId, and adequately long to be diff --git a/app/common/schema.ts b/app/common/schema.ts index dfe7268f..853141a6 100644 --- a/app/common/schema.ts +++ b/app/common/schema.ts @@ -4,7 +4,7 @@ import { GristObjCode } from "app/plugin/GristData"; // tslint:disable:object-literal-key-quotes -export const SCHEMA_VERSION = 39; +export const SCHEMA_VERSION = 40; export const schema = { @@ -23,6 +23,7 @@ export const schema = { summarySourceTable : "Ref:_grist_Tables", onDemand : "Bool", rawViewSectionRef : "Ref:_grist_Views_section", + recordCardViewSectionRef: "Ref:_grist_Views_section", }, "_grist_Tables_column": { @@ -234,6 +235,7 @@ export interface SchemaTypes { summarySourceTable: number; onDemand: boolean; rawViewSectionRef: number; + recordCardViewSectionRef: number; }; "_grist_Tables_column": { diff --git a/app/server/lib/initialDocSql.ts b/app/server/lib/initialDocSql.ts index 0508dec2..d853adcc 100644 --- a/app/server/lib/initialDocSql.ts +++ b/app/server/lib/initialDocSql.ts @@ -6,8 +6,8 @@ export const GRIST_DOC_SQL = ` PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE IF NOT EXISTS "_grist_DocInfo" (id INTEGER PRIMARY KEY, "docId" TEXT DEFAULT '', "peers" TEXT DEFAULT '', "basketId" TEXT DEFAULT '', "schemaVersion" INTEGER DEFAULT 0, "timezone" TEXT DEFAULT '', "documentSettings" TEXT DEFAULT ''); -INSERT INTO _grist_DocInfo VALUES(1,'','','',39,'',''); -CREATE TABLE IF NOT EXISTS "_grist_Tables" (id INTEGER PRIMARY KEY, "tableId" TEXT DEFAULT '', "primaryViewId" INTEGER DEFAULT 0, "summarySourceTable" INTEGER DEFAULT 0, "onDemand" BOOLEAN DEFAULT 0, "rawViewSectionRef" INTEGER DEFAULT 0); +INSERT INTO _grist_DocInfo VALUES(1,'','','',40,'',''); +CREATE TABLE IF NOT EXISTS "_grist_Tables" (id INTEGER PRIMARY KEY, "tableId" TEXT DEFAULT '', "primaryViewId" INTEGER DEFAULT 0, "summarySourceTable" INTEGER DEFAULT 0, "onDemand" BOOLEAN DEFAULT 0, "rawViewSectionRef" INTEGER DEFAULT 0, "recordCardViewSectionRef" INTEGER DEFAULT 0); CREATE TABLE IF NOT EXISTS "_grist_Tables_column" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colId" TEXT DEFAULT '', "type" TEXT DEFAULT '', "widgetOptions" TEXT DEFAULT '', "isFormula" BOOLEAN DEFAULT 0, "formula" TEXT DEFAULT '', "label" TEXT DEFAULT '', "description" TEXT DEFAULT '', "untieColIdFromLabel" BOOLEAN DEFAULT 0, "summarySourceCol" INTEGER DEFAULT 0, "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "rules" TEXT DEFAULT NULL, "recalcWhen" INTEGER DEFAULT 0, "recalcDeps" TEXT DEFAULT NULL); CREATE TABLE IF NOT EXISTS "_grist_Imports" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "origFileName" TEXT DEFAULT '', "parseFormula" TEXT DEFAULT '', "delimiter" TEXT DEFAULT '', "doublequote" BOOLEAN DEFAULT 0, "escapechar" TEXT DEFAULT '', "quotechar" TEXT DEFAULT '', "skipinitialspace" BOOLEAN DEFAULT 0, "encoding" TEXT DEFAULT '', "hasHeaders" BOOLEAN DEFAULT 0); CREATE TABLE IF NOT EXISTS "_grist_External_database" (id INTEGER PRIMARY KEY, "host" TEXT DEFAULT '', "port" INTEGER DEFAULT 0, "username" TEXT DEFAULT '', "dialect" TEXT DEFAULT '', "database" TEXT DEFAULT '', "storage" TEXT DEFAULT ''); @@ -43,9 +43,9 @@ export const GRIST_DOC_WITH_TABLE1_SQL = ` PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE IF NOT EXISTS "_grist_DocInfo" (id INTEGER PRIMARY KEY, "docId" TEXT DEFAULT '', "peers" TEXT DEFAULT '', "basketId" TEXT DEFAULT '', "schemaVersion" INTEGER DEFAULT 0, "timezone" TEXT DEFAULT '', "documentSettings" TEXT DEFAULT ''); -INSERT INTO _grist_DocInfo VALUES(1,'','','',39,'',''); -CREATE TABLE IF NOT EXISTS "_grist_Tables" (id INTEGER PRIMARY KEY, "tableId" TEXT DEFAULT '', "primaryViewId" INTEGER DEFAULT 0, "summarySourceTable" INTEGER DEFAULT 0, "onDemand" BOOLEAN DEFAULT 0, "rawViewSectionRef" INTEGER DEFAULT 0); -INSERT INTO _grist_Tables VALUES(1,'Table1',1,0,0,2); +INSERT INTO _grist_DocInfo VALUES(1,'','','',40,'',''); +CREATE TABLE IF NOT EXISTS "_grist_Tables" (id INTEGER PRIMARY KEY, "tableId" TEXT DEFAULT '', "primaryViewId" INTEGER DEFAULT 0, "summarySourceTable" INTEGER DEFAULT 0, "onDemand" BOOLEAN DEFAULT 0, "rawViewSectionRef" INTEGER DEFAULT 0, "recordCardViewSectionRef" INTEGER DEFAULT 0); +INSERT INTO _grist_Tables VALUES(1,'Table1',1,0,0,2,3); CREATE TABLE IF NOT EXISTS "_grist_Tables_column" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colId" TEXT DEFAULT '', "type" TEXT DEFAULT '', "widgetOptions" TEXT DEFAULT '', "isFormula" BOOLEAN DEFAULT 0, "formula" TEXT DEFAULT '', "label" TEXT DEFAULT '', "description" TEXT DEFAULT '', "untieColIdFromLabel" BOOLEAN DEFAULT 0, "summarySourceCol" INTEGER DEFAULT 0, "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "rules" TEXT DEFAULT NULL, "recalcWhen" INTEGER DEFAULT 0, "recalcDeps" TEXT DEFAULT NULL); INSERT INTO _grist_Tables_column VALUES(1,1,1,'manualSort','ManualSortPos','',0,'','manualSort','',0,0,0,0,NULL,0,NULL); INSERT INTO _grist_Tables_column VALUES(2,1,2,'A','Any','',1,'','A','',0,0,0,0,NULL,0,NULL); @@ -65,6 +65,7 @@ INSERT INTO _grist_Views VALUES(1,'Table1','raw_data',''); CREATE TABLE IF NOT EXISTS "_grist_Views_section" (id INTEGER PRIMARY KEY, "tableRef" INTEGER DEFAULT 0, "parentId" INTEGER DEFAULT 0, "parentKey" TEXT DEFAULT '', "title" TEXT DEFAULT '', "description" TEXT DEFAULT '', "defaultWidth" INTEGER DEFAULT 0, "borderWidth" INTEGER DEFAULT 0, "theme" TEXT DEFAULT '', "options" TEXT DEFAULT '', "chartType" TEXT DEFAULT '', "layoutSpec" TEXT DEFAULT '', "filterSpec" TEXT DEFAULT '', "sortColRefs" TEXT DEFAULT '', "linkSrcSectionRef" INTEGER DEFAULT 0, "linkSrcColRef" INTEGER DEFAULT 0, "linkTargetColRef" INTEGER DEFAULT 0, "embedId" TEXT DEFAULT '', "rules" TEXT DEFAULT NULL); INSERT INTO _grist_Views_section VALUES(1,1,1,'record','','',100,1,'','','','','','[]',0,0,0,'',NULL); INSERT INTO _grist_Views_section VALUES(2,1,0,'record','','',100,1,'','','','','','',0,0,0,'',NULL); +INSERT INTO _grist_Views_section VALUES(3,1,0,'single','','',100,1,'','','','','','',0,0,0,'',NULL); CREATE TABLE IF NOT EXISTS "_grist_Views_section_field" (id INTEGER PRIMARY KEY, "parentId" INTEGER DEFAULT 0, "parentPos" NUMERIC DEFAULT 1e999, "colRef" INTEGER DEFAULT 0, "width" INTEGER DEFAULT 0, "widgetOptions" TEXT DEFAULT '', "displayCol" INTEGER DEFAULT 0, "visibleCol" INTEGER DEFAULT 0, "filter" TEXT DEFAULT '', "rules" TEXT DEFAULT NULL); INSERT INTO _grist_Views_section_field VALUES(1,1,1,2,0,'',0,0,'',NULL); INSERT INTO _grist_Views_section_field VALUES(2,1,2,3,0,'',0,0,'',NULL); @@ -72,6 +73,9 @@ INSERT INTO _grist_Views_section_field VALUES(3,1,3,4,0,'',0,0,'',NULL); INSERT INTO _grist_Views_section_field VALUES(4,2,4,2,0,'',0,0,'',NULL); INSERT INTO _grist_Views_section_field VALUES(5,2,5,3,0,'',0,0,'',NULL); INSERT INTO _grist_Views_section_field VALUES(6,2,6,4,0,'',0,0,'',NULL); +INSERT INTO _grist_Views_section_field VALUES(7,3,7,2,0,'',0,0,'',NULL); +INSERT INTO _grist_Views_section_field VALUES(8,3,8,3,0,'',0,0,'',NULL); +INSERT INTO _grist_Views_section_field VALUES(9,3,9,4,0,'',0,0,'',NULL); CREATE TABLE IF NOT EXISTS "_grist_Validations" (id INTEGER PRIMARY KEY, "formula" TEXT DEFAULT '', "name" TEXT DEFAULT '', "tableRef" INTEGER DEFAULT 0); CREATE TABLE IF NOT EXISTS "_grist_REPL_Hist" (id INTEGER PRIMARY KEY, "code" TEXT DEFAULT '', "outputText" TEXT DEFAULT '', "errorText" TEXT DEFAULT ''); CREATE TABLE IF NOT EXISTS "_grist_Attachments" (id INTEGER PRIMARY KEY, "fileIdent" TEXT DEFAULT '', "fileName" TEXT DEFAULT '', "fileType" TEXT DEFAULT '', "fileSize" INTEGER DEFAULT 0, "fileExt" TEXT DEFAULT '', "imageHeight" INTEGER DEFAULT 0, "imageWidth" INTEGER DEFAULT 0, "timeDeleted" DATETIME DEFAULT NULL, "timeUploaded" DATETIME DEFAULT NULL); diff --git a/app/server/lib/sendAppPage.ts b/app/server/lib/sendAppPage.ts index 6215efc9..991b39fb 100644 --- a/app/server/lib/sendAppPage.ts +++ b/app/server/lib/sendAppPage.ts @@ -86,6 +86,7 @@ export function makeGristConfig(options: MakeGristConfigOptions): GristLoadConfi deploymentType: server?.getDeploymentType(), templateOrg: getTemplateOrg(), canCloseAccount: isAffirmative(process.env.GRIST_ACCOUNT_CLOSE), + experimentalPlugins: isAffirmative(process.env.GRIST_EXPERIMENTAL_PLUGINS), ...extra, }; } diff --git a/buildtools/fly-template.toml b/buildtools/fly-template.toml index 2345f059..4eba32ff 100644 --- a/buildtools/fly-template.toml +++ b/buildtools/fly-template.toml @@ -10,6 +10,7 @@ processes = [] APP_STATIC_URL="https://{APP_NAME}.fly.dev" ALLOWED_WEBHOOK_DOMAINS="webhook.site" PERMITTED_CUSTOM_WIDGETS="calendar" + GRIST_EXPERIMENTAL_PLUGINS="1" GRIST_SINGLE_ORG="docs" PORT = "8080" FLY_DEPLOY_EXPIRATION = "{FLY_DEPLOY_EXPIRATION}" diff --git a/sandbox/grist/docmodel.py b/sandbox/grist/docmodel.py index 4197c1bb..9083368e 100644 --- a/sandbox/grist/docmodel.py +++ b/sandbox/grist/docmodel.py @@ -141,6 +141,9 @@ class MetaTableExtras(object): def isRaw(rec, table): return rec.tableRef.rawViewSectionRef == rec + def isRecordCard(rec, table): + return rec.tableRef.recordCardViewSectionRef == rec + class _grist_Filters(object): def setAutoRemove(rec, table): """Marks the filter for removal if its column no longer exists.""" diff --git a/sandbox/grist/migrations.py b/sandbox/grist/migrations.py index 419b9f1b..b971e0e5 100644 --- a/sandbox/grist/migrations.py +++ b/sandbox/grist/migrations.py @@ -1235,3 +1235,57 @@ def migration39(tdset): if 'description' not in tdset.all_tables['_grist_Views_section'].columns: doc_actions.append(add_column('_grist_Views_section', 'description', 'Text')) return tdset.apply_doc_actions(doc_actions) + +@migration(schema_version=40) +def migration40(tdset): + """ + Adds a recordCardViewSectionRef column to _grist_Tables, populating it + for each non-summary table in _grist_Tables that has a rawViewSectionRef. + """ + doc_actions = [ + add_column( + '_grist_Tables', + 'recordCardViewSectionRef', + 'Ref:_grist_Views_section' + ), + ] + + tables = list(actions.transpose_bulk_action(tdset.all_tables["_grist_Tables"])) + columns = list(actions.transpose_bulk_action(tdset.all_tables["_grist_Tables_column"])) + + new_view_section_id = next_id(tdset, "_grist_Views_section") + + for table in sorted(tables, key=lambda t: t.tableId): + if not table.rawViewSectionRef or table.summarySourceTable: + continue + + table_columns = [ + col for col in columns + if table.id == col.parentId and is_visible_column(col.colId) + ] + table_columns.sort(key=lambda c: c.parentPos) + fields = { + "parentId": [new_view_section_id] * len(table_columns), + "colRef": [col.id for col in table_columns], + "parentPos": [col.parentPos for col in table_columns], + } + field_ids = [None] * len(table_columns) + + doc_actions += [ + actions.AddRecord("_grist_Views_section", new_view_section_id, { + "tableRef": table.id, + "parentId": 0, + "parentKey": "single", + "title": "", + "defaultWidth": 100, + "borderWidth": 1, + }), + actions.UpdateRecord("_grist_Tables", table.id, { + "recordCardViewSectionRef": new_view_section_id, + }), + actions.BulkAddRecord("_grist_Views_section_field", field_ids, fields), + ] + + new_view_section_id += 1 + + return tdset.apply_doc_actions(doc_actions) diff --git a/sandbox/grist/schema.py b/sandbox/grist/schema.py index 76897f0b..0e4ecdde 100644 --- a/sandbox/grist/schema.py +++ b/sandbox/grist/schema.py @@ -15,7 +15,7 @@ import six import actions -SCHEMA_VERSION = 39 +SCHEMA_VERSION = 40 def make_column(col_id, col_type, formula='', isFormula=False): return { @@ -58,6 +58,7 @@ def schema_create_actions(): make_column("onDemand", "Bool"), make_column("rawViewSectionRef", "Ref:_grist_Views_section"), + make_column("recordCardViewSectionRef", "Ref:_grist_Views_section"), ]), # All columns in all user tables. diff --git a/sandbox/grist/summary.py b/sandbox/grist/summary.py index 9df76a7f..178e0215 100644 --- a/sandbox/grist/summary.py +++ b/sandbox/grist/summary.py @@ -202,7 +202,8 @@ class SummaryActions(object): encode_summary_table_name(source_table.tableId, groupby_col_ids), [get_colinfo_dict(ci, with_id=True) for ci in groupby_colinfo + formula_colinfo], summarySourceTableRef=source_table.id, - raw_section=True) + raw_section=True, + record_card_section=False) summary_table = self.docmodel.tables.table.get_record(result['id']) created = True # Note that in this case, _get_or_add_columns() below should not add any new columns, diff --git a/sandbox/grist/test_column_actions.py b/sandbox/grist/test_column_actions.py index b69b48e9..dde2363a 100644 --- a/sandbox/grist/test_column_actions.py +++ b/sandbox/grist/test_column_actions.py @@ -223,15 +223,15 @@ class TestColumnActions(test_engine.EngineTestCase): Field(2, colRef=12), Field(3, colRef=13), ]), - Section(4, parentKey="record", tableRef=2, fields=[ - Field(10, colRef=15), - Field(11, colRef=16), - Field(12, colRef=17), + Section(5, parentKey="record", tableRef=2, fields=[ + Field(13, colRef=15), + Field(14, colRef=16), + Field(15, colRef=17), ]), - Section(6, parentKey="record", tableRef=3, fields=[ - Field(16, colRef=18), - Field(17, colRef=20), - Field(18, colRef=21), + Section(7, parentKey="record", tableRef=3, fields=[ + Field(19, colRef=18), + Field(20, colRef=20), + Field(21, colRef=21), ]), ]), View(2, sections=[ @@ -311,14 +311,14 @@ class TestColumnActions(test_engine.EngineTestCase): Field(2, colRef=12), Field(3, colRef=13), ]), - Section(4, parentKey="record", tableRef=2, fields=[ - Field(10, colRef=15), - Field(12, colRef=17), + Section(5, parentKey="record", tableRef=2, fields=[ + Field(13, colRef=15), + Field(15, colRef=17), ]), - Section(6, parentKey="record", tableRef=3, fields=[ - Field(16, colRef=18), - Field(17, colRef=20), - Field(18, colRef=21), + Section(7, parentKey="record", tableRef=3, fields=[ + Field(19, colRef=18), + Field(20, colRef=20), + Field(21, colRef=21), ]), ]), View(2, sections=[ @@ -368,13 +368,13 @@ class TestColumnActions(test_engine.EngineTestCase): Section(1, parentKey="record", tableRef=1, fields=[ Field(3, colRef=13), ]), - Section(4, parentKey="record", tableRef=2, fields=[ - Field(10, colRef=15), - Field(12, colRef=17), + Section(5, parentKey="record", tableRef=2, fields=[ + Field(13, colRef=15), + Field(15, colRef=17), ]), - Section(6, parentKey="record", tableRef=4, fields=[ - Field(17, colRef=23), - Field(18, colRef=24), + Section(7, parentKey="record", tableRef=4, fields=[ + Field(20, colRef=23), + Field(21, colRef=24), ]), ]), View(2, sections=[ @@ -420,14 +420,14 @@ class TestColumnActions(test_engine.EngineTestCase): self.init_sample_data() # Add sortSpecs to ViewSections. - self.apply_user_action(['BulkUpdateRecord', '_grist_Views_section', [2, 3, 4], + self.apply_user_action(['BulkUpdateRecord', '_grist_Views_section', [2, 3, 5], {'sortColRefs': ['[15, -16]', '[-15, 16, 17]', '[19]']} ]) self.assertTableData('_grist_Views_section', cols="subset", rows="subset", data=[ ["id", "sortColRefs" ], [2, '[15, -16]' ], [3, '[-15, 16, 17]'], - [4, '[19]' ], + [5, '[19]' ], ]) # Remove column, and check that the correct sortColRefs items are removed. @@ -436,7 +436,7 @@ class TestColumnActions(test_engine.EngineTestCase): ["id", "sortColRefs"], [2, '[15]' ], [3, '[-15, 17]' ], - [4, '[19]' ], + [5, '[19]' ], ]) # Update sortColRefs for next test. @@ -450,5 +450,5 @@ class TestColumnActions(test_engine.EngineTestCase): ["id", "sortColRefs"], [2, '[]' ], [3, '[-16]' ], - [4, '[]' ], + [5, '[]' ], ]) diff --git a/sandbox/grist/test_display_cols.py b/sandbox/grist/test_display_cols.py index 7729e042..789f567e 100644 --- a/sandbox/grist/test_display_cols.py +++ b/sandbox/grist/test_display_cols.py @@ -53,6 +53,7 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 0], [2, 25, 0], + [3, 25, 0], ]) self.assertTableData("Favorites", cols="subset", data=[ ["id", "favorite"], @@ -70,6 +71,7 @@ class TestUserActions(test_engine.EngineTestCase): [1, 25, 0], [2, 25, 0], [3, 25, 0], + [4, 25, 0], ]) # Set display formula for 'favorite' column. @@ -86,7 +88,7 @@ class TestUserActions(test_engine.EngineTestCase): # A single "gristHelper_Display2" column should be added with the requested formula, since both # require the same formula. The fields' colRefs should be set to the new column. self.apply_user_action(['SetDisplayFormula', 'Favorites', 1, None, '$favorite.network']) - self.apply_user_action(['SetDisplayFormula', 'Favorites', 3, None, '$favorite.network']) + self.apply_user_action(['SetDisplayFormula', 'Favorites', 4, None, '$favorite.network']) self.assertTableData("_grist_Tables_column", cols="subset", rows=(lambda r: r.id >= 25), data=[ ["id", "colId", "parentId", "displayCol", "formula"], [25, "favorite", 2, 26, ""], @@ -97,13 +99,14 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 27], [2, 25, 0], - [3, 25, 27], + [3, 25, 0], + [4, 25, 27], ]) # Change display formula for a field. # Since the field is changing to use a formula not yet held by a display column, # a new display column should be added with the desired formula. - self.apply_user_action(['SetDisplayFormula', 'Favorites', 3, None, '$favorite.viewers']) + self.apply_user_action(['SetDisplayFormula', 'Favorites', 4, None, '$favorite.viewers']) self.assertTableData("_grist_Tables_column", cols="subset", rows=(lambda r: r.id >= 25), data=[ ["id", "colId", "parentId", "displayCol", "formula"], [25, "favorite", 2, 26, ""], @@ -115,13 +118,14 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 27], [2, 25, 0], - [3, 25, 28], + [3, 25, 0], + [4, 25, 28], ]) # Remove a field. # This should also remove the display column used by that field, since it is not used # by any other fields. - self.apply_user_action(['RemoveRecord', '_grist_Views_section_field', 3]) + self.apply_user_action(['RemoveRecord', '_grist_Views_section_field', 4]) self.assertTableData("_grist_Tables_column", cols="subset", rows=(lambda r: r.id >= 25), data=[ ["id", "colId", "parentId", "displayCol", "formula"], [25, "favorite", 2, 26, ""], @@ -132,6 +136,7 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 27], [2, 25, 0], + [3, 25, 0], ]) # Add a new column with a formula. @@ -145,7 +150,7 @@ class TestUserActions(test_engine.EngineTestCase): 'parentId': 3, 'colRef': 25 }]) - self.apply_user_action(['SetDisplayFormula', 'Favorites', 6, None, '$favorite.viewers']) + self.apply_user_action(['SetDisplayFormula', 'Favorites', 8, None, '$favorite.viewers']) self.assertTableData("_grist_Tables_column", cols="subset", rows=(lambda r: r.id >= 25), data=[ ["id", "colId", "parentId", "displayCol", "formula"], [25, "favorite", 2, 26, ""], @@ -158,17 +163,19 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 27], [2, 25, 0], - [3, 28, 0], # fav_viewers field - [4, 28, 0], # fav_viewers field - [5, 28, 0], # fav_viewers field - [6, 25, 29] # re-added field w/ display col + [3, 25, 0], + [4, 28, 0], # fav_viewers field + [5, 28, 0], # fav_viewers field + [6, 28, 0], # fav_viewers field + [7, 28, 0], # re-added field w/ display col + [8, 25, 29], # fav_viewers field ]) # Change the display formula for a field to be the same as the other field, then remove # the field. # The display column should not be removed since it is still in use. - self.apply_user_action(['SetDisplayFormula', 'Favorites', 6, None, '$favorite.network']) - self.apply_user_action(['RemoveRecord', '_grist_Views_section_field', 6]) + self.apply_user_action(['SetDisplayFormula', 'Favorites', 8, None, '$favorite.network']) + self.apply_user_action(['RemoveRecord', '_grist_Views_section_field', 8]) self.assertTableData("_grist_Tables_column", cols="subset", rows=(lambda r: r.id >= 25), data=[ ["id", "colId", "parentId", "displayCol", "formula"], [25, "favorite", 2, 26, ""], @@ -180,9 +187,11 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 27], [2, 25, 0], - [3, 28, 0], + [3, 25, 0], [4, 28, 0], [5, 28, 0], + [6, 28, 0], + [7, 28, 0], ]) # Clear field display formula, then set it again. @@ -199,9 +208,11 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 0], [2, 25, 0], - [3, 28, 0], + [3, 25, 0], [4, 28, 0], [5, 28, 0], + [6, 28, 0], + [7, 28, 0], ]) # Setting the display formula should add another display column. self.apply_user_action(['SetDisplayFormula', 'Favorites', 1, None, '$favorite.viewers']) @@ -216,9 +227,11 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 29], [2, 25, 0], - [3, 28, 0], + [3, 25, 0], [4, 28, 0], [5, 28, 0], + [6, 28, 0], + [7, 28, 0], ]) # Change column display formula. @@ -235,9 +248,11 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "colRef", "displayCol"], [1, 25, 29], [2, 25, 0], - [3, 28, 0], + [3, 25, 0], [4, 28, 0], [5, 28, 0], + [6, 28, 0], + [7, 28, 0], ]) # Remove column. @@ -249,9 +264,10 @@ class TestUserActions(test_engine.EngineTestCase): ]) self.assertTableData("_grist_Views_section_field", cols="subset", data=[ ["id", "colRef", "displayCol"], - [3, 28, 0], [4, 28, 0], [5, 28, 0], + [6, 28, 0], + [7, 28, 0], ]) @@ -381,7 +397,7 @@ class TestUserActions(test_engine.EngineTestCase): # pylint:disable=line-too-long self.assertOutActions(out_actions, { "stored": [ - ["BulkRemoveRecord", "_grist_Views_section_field", [2, 4]], + ["BulkRemoveRecord", "_grist_Views_section_field", [2, 4, 6]], ["BulkRemoveRecord", "_grist_Tables_column", [26, 27]], ["RemoveColumn", "People", "favorite"], ["RemoveColumn", "People", "gristHelper_Display"], @@ -392,7 +408,7 @@ class TestUserActions(test_engine.EngineTestCase): "undo": [ ["BulkUpdateRecord", "People", [1, 2, 3], {"gristHelper_Display2": ["Netflix", "HBO", "NBC"]}], ["BulkUpdateRecord", "People", [1, 2, 3], {"gristHelper_Display": ["Narcos", "Game of Thrones", "Today"]}], - ["BulkAddRecord", "_grist_Views_section_field", [2, 4], {"colRef": [26, 26], "displayCol": [28, 0], "parentId": [1, 2], "parentPos": [2.0, 4.0]}], + ["BulkAddRecord", "_grist_Views_section_field", [2, 4, 6], {"colRef": [26, 26, 26], "displayCol": [28, 0, 0], "parentId": [1, 2, 3], "parentPos": [2.0, 4.0, 6.0]}], ["BulkAddRecord", "_grist_Tables_column", [26, 27], {"colId": ["favorite", "gristHelper_Display"], "displayCol": [27, 0], "formula": ["", "$favorite.show"], "isFormula": [False, True], "label": ["favorite", "gristHelper_Display"], "parentId": [2, 2], "parentPos": [6.0, 7.0], "type": ["Ref:Television", "Any"], "widgetOptions": ["\"{\"alignment\":\"center\",\"visibleCol\":\"show\"}\"", ""]}], ["BulkUpdateRecord", "People", [1, 2, 3], {"favorite": [12, 11, 13]}], ["AddColumn", "People", "favorite", {"formula": "", "isFormula": False, "type": "Ref:Television"}], diff --git a/sandbox/grist/test_docmodel.py b/sandbox/grist/test_docmodel.py index 4f78598c..f49e1c81 100644 --- a/sandbox/grist/test_docmodel.py +++ b/sandbox/grist/test_docmodel.py @@ -141,35 +141,42 @@ class TestDocModel(test_engine.EngineTestCase): self.assertPartialData('_grist_Views_section', ["id", "parentId", "tableRef"], [ [1, 1, 4], [2, 0, 4], - [3, 2, 5], - [4, 0, 5], - [5, 1, 4], - [6, 1, 5], + [3, 0, 4], + [4, 2, 5], + [5, 0, 5], + [6, 0, 5], + [7, 1, 4], + [8, 1, 5], ]) self.assertPartialData('_grist_Views_section_field', ["id", "parentId", "parentPos"], [ - [1, 1, 1.0], - [2, 1, 2.0], - [3, 2, 3.0], - [4, 2, 4.0], - [5, 3, 5.0], - [6, 3, 6.0], - [7, 4, 7.0], - [8, 4, 8.0], - [9, 5, 9.0], + [1, 1, 1.0], + [2, 1, 2.0], + [3, 2, 3.0], + [4, 2, 4.0], + [5, 3, 5.0], + [6, 3, 6.0], + [7, 4, 7.0], + [8, 4, 8.0], + [9, 5, 9.0], [10, 5, 10.0], [11, 6, 11.0], [12, 6, 12.0], + [13, 7, 13.0], + [14, 7, 14.0], + [15, 8, 15.0], + [16, 8, 16.0], ]) table = self.engine.docmodel.tables.lookupOne(tableId='Test2') - self.assertRecordSet(table.viewSections, [1, 2, 5]) + self.assertRecordSet(table.viewSections, [1, 2, 3, 7]) self.assertRecordSet(list(table.viewSections)[0].fields, [1, 2]) - self.assertRecordSet(list(table.viewSections)[2].fields, [9, 10]) + self.assertRecordSet(list(table.viewSections)[3].fields, [13, 14]) view = self.engine.docmodel.views.lookupOne(id=1) - self.assertRecordSet(view.viewSections, [1, 5, 6]) + self.assertRecordSet(view.viewSections, [1, 7, 8]) - self.engine.docmodel.remove(set(table.viewSections) - {table.rawViewSectionRef}) - self.assertRecordSet(view.viewSections, [6]) + self.engine.docmodel.remove(set(table.viewSections) - + {table.rawViewSectionRef, table.recordCardViewSectionRef}) + self.assertRecordSet(view.viewSections, [8]) def test_modifications(self): diff --git a/sandbox/grist/test_import_actions.py b/sandbox/grist/test_import_actions.py index c7e919ed..a456562c 100644 --- a/sandbox/grist/test_import_actions.py +++ b/sandbox/grist/test_import_actions.py @@ -54,10 +54,13 @@ class TestImportActions(test_engine.EngineTestCase): self.assertPartialData("_grist_Views_section", ["id", "tableRef", 'fields'], [ [1, 1, [1, 2, 3]], # section for "Source" table [2, 1, [4, 5, 6]], # section for "Source" table - [3, 2, [7, 8]], # section for "Destination1" table - [4, 2, [9, 10]], # section for "Destination1" table - [5, 3, [11]], # section for "Destination2" table - [6, 3, [12]], # section for "Destination2" table + [3, 1, [7, 8, 9]], # section for "Source" table + [4, 2, [10, 11]], # section for "Destination1" table + [5, 2, [12, 13]], # section for "Destination1" table + [6, 2, [14, 15]], # section for "Destination1" table + [7, 3, [16]], # section for "Destination2" table + [8, 3, [17]], # section for "Destination2" table + [9, 3, [18]], # section for "Destination2" table ]) def test_transform(self): @@ -89,11 +92,14 @@ class TestImportActions(test_engine.EngineTestCase): self.assertPartialData("_grist_Views_section", ["id", "tableRef", 'fields'], [ [1, 1, [1, 2, 3]], [2, 1, [4, 5, 6]], - [3, 2, [7, 8]], - [4, 2, [9, 10]], - [5, 3, [11]], - [6, 3, [12]], - [7, 1, [13, 14]], # new section for transform preview + [3, 1, [7, 8, 9]], + [4, 2, [10, 11]], + [5, 2, [12, 13]], + [6, 2, [14, 15]], + [7, 3, [16]], + [8, 3, [17]], + [9, 3, [18]], + [10, 1, [19, 20]], # new section for transform preview ]) # Apply useraction again to verify that old columns and sections are removing @@ -117,17 +123,20 @@ class TestImportActions(test_engine.EngineTestCase): [2, "Alison", "Boston", 7003, "", 2.0], ]) self.assertPartialData("_grist_Views_section", ["id", "tableRef", 'fields'], [ - [1, 1, [1, 2, 3]], - [2, 1, [4, 5, 6]], - [3, 2, [7, 8]], - [4, 2, [9, 10]], - [5, 3, [11]], - [6, 3, [12]], - [7, 1, [13]], # new section for transform preview + [1, 1, [1, 2, 3]], + [2, 1, [4, 5, 6]], + [3, 1, [7, 8, 9]], + [4, 2, [10, 11]], + [5, 2, [12, 13]], + [6, 2, [14, 15]], + [7, 3, [16]], + [8, 3, [17]], + [9, 3, [18]], + [10, 1, [19]], # new section for transform preview ]) - def test_regenereate_importer_view(self): + def test_regenerate_importer_view(self): # Generate without a destination table, and then with one. Ensure that we don't omit the # actions needed to populate the table in the second call. self.init_state() @@ -135,8 +144,8 @@ class TestImportActions(test_engine.EngineTestCase): out_actions = self.apply_user_action(['GenImporterView', 'Source', 'Destination1', None, {}]) self.assertPartialOutActions(out_actions, { "stored": [ - ["BulkRemoveRecord", "_grist_Views_section_field", [13, 14, 15]], - ["RemoveRecord", "_grist_Views_section", 7], + ["BulkRemoveRecord", "_grist_Views_section_field", [19, 20, 21]], + ["RemoveRecord", "_grist_Views_section", 10], ["BulkRemoveRecord", "_grist_Tables_column", [10, 11, 12]], ["RemoveColumn", "Source", "gristHelper_Import_Name"], ["RemoveColumn", "Source", "gristHelper_Import_City"], @@ -145,8 +154,8 @@ class TestImportActions(test_engine.EngineTestCase): ["AddRecord", "_grist_Tables_column", 10, {"colId": "gristHelper_Import_Name", "formula": "$Name", "isFormula": True, "label": "Name", "parentId": 1, "parentPos": 10.0, "type": "Text", "widgetOptions": ""}], ["AddColumn", "Source", "gristHelper_Import_City", {"formula": "$City", "isFormula": True, "type": "Text"}], ["AddRecord", "_grist_Tables_column", 11, {"colId": "gristHelper_Import_City", "formula": "$City", "isFormula": True, "label": "City", "parentId": 1, "parentPos": 11.0, "type": "Text", "widgetOptions": ""}], - ["AddRecord", "_grist_Views_section", 7, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "sortColRefs": "[]", "tableRef": 1}], - ["BulkAddRecord", "_grist_Views_section_field", [13, 14], {"colRef": [10, 11], "parentId": [7, 7], "parentPos": [13.0, 14.0]}], + ["AddRecord", "_grist_Views_section", 10, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "sortColRefs": "[]", "tableRef": 1}], + ["BulkAddRecord", "_grist_Views_section_field", [19, 20], {"colRef": [10, 11], "parentId": [10, 10], "parentPos": [19.0, 20.0]}], # The actions to populate the removed and re-added columns should be there. ["BulkUpdateRecord", "Source", [1, 2], {"gristHelper_Import_City": ["New York", "Boston"]}], ["BulkUpdateRecord", "Source", [1, 2], {"gristHelper_Import_Name": ["John", "Alison"]}], @@ -181,11 +190,14 @@ class TestImportActions(test_engine.EngineTestCase): [2, "Alison", "Boston", 7003, "Alison", "Boston", 7003, 2.0], ]) self.assertPartialData("_grist_Views_section", ["id", "tableRef", 'fields'], [ - [1, 1, [1, 2, 3]], - [2, 1, [4, 5, 6]], - [3, 2, [7, 8]], - [4, 2, [9, 10]], - [5, 3, [11]], - [6, 3, [12]], - [7, 1, [13, 14, 15]], # new section for transform preview + [1, 1, [1, 2, 3]], + [2, 1, [4, 5, 6]], + [3, 1, [7, 8, 9]], + [4, 2, [10, 11]], + [5, 2, [12, 13]], + [6, 2, [14, 15]], + [7, 3, [16]], + [8, 3, [17]], + [9, 3, [18]], + [10, 1, [19, 20, 21]], # new section for transform preview ]) diff --git a/sandbox/grist/test_table_actions.py b/sandbox/grist/test_table_actions.py index 42f98724..71557c3c 100644 --- a/sandbox/grist/test_table_actions.py +++ b/sandbox/grist/test_table_actions.py @@ -88,27 +88,27 @@ class TestTableActions(test_engine.EngineTestCase): ]), ]), View(2, sections=[ - Section(3, parentKey="record", tableRef=2, fields=[ - Field(7, colRef=6), - Field(8, colRef=7), - Field(9, colRef=8), + Section(4, parentKey="record", tableRef=2, fields=[ + Field(10, colRef=6), + Field(11, colRef=7), + Field(12, colRef=8), ]), ]), View(3, sections=[ - Section(5, parentKey="record", tableRef=1, fields=[ - Field(13, colRef=2), - Field(14, colRef=3), - Field(15, colRef=4), + Section(7, parentKey="record", tableRef=1, fields=[ + Field(19, colRef=2), + Field(20, colRef=3), + Field(21, colRef=4), ]), - Section(7, parentKey="record", tableRef=3, fields=[ - Field(19, colRef=9), - Field(20, colRef=11), - Field(21, colRef=12), + Section(9, parentKey="record", tableRef=3, fields=[ + Field(25, colRef=9), + Field(26, colRef=11), + Field(27, colRef=12), ]), - Section(8, parentKey="record", tableRef=2, fields=[ - Field(22, colRef=6), - Field(23, colRef=7), - Field(24, colRef=8), + Section(10, parentKey="record", tableRef=2, fields=[ + Field(28, colRef=6), + Field(29, colRef=7), + Field(30, colRef=8), ]), ]), ]) @@ -295,17 +295,17 @@ class TestTableActions(test_engine.EngineTestCase): ]) self.assertViews([ View(2, sections=[ - Section(3, parentKey="record", tableRef=2, fields=[ - Field(7, colRef=6), - Field(8, colRef=7), - Field(9, colRef=8), + Section(4, parentKey="record", tableRef=2, fields=[ + Field(10, colRef=6), + Field(11, colRef=7), + Field(12, colRef=8), ]), ]), View(3, sections=[ - Section(8, parentKey="record", tableRef=2, fields=[ - Field(22, colRef=6), - Field(23, colRef=7), - Field(24, colRef=8), + Section(10, parentKey="record", tableRef=2, fields=[ + Field(28, colRef=6), + Field(29, colRef=7), + Field(30, colRef=8), ]), ]), ]) diff --git a/sandbox/grist/test_useractions.py b/sandbox/grist/test_useractions.py index 5fd54f41..7ed2ee5a 100644 --- a/sandbox/grist/test_useractions.py +++ b/sandbox/grist/test_useractions.py @@ -58,6 +58,7 @@ class TestUserActions(test_engine.EngineTestCase): self.assertPartialData("_grist_Views_section_field", ["id", "colRef", "widgetOptions"], [ [1, 23, ""], [2, 23, ""], + [3, 23, ""], ]) self.assertPartialData("Schools", ["id", "city"], [ [1, "New York" ], @@ -78,8 +79,11 @@ class TestUserActions(test_engine.EngineTestCase): 'grist_Transform', 'formula': 'return $city', 'label': 'grist_Transform', 'type': 'Text' }], - ["AddRecord", "_grist_Views_section_field", 3, { - "colRef": 24, "parentId": 2, "parentPos": 3.0 + ["AddRecord", "_grist_Views_section_field", 4, { + "colRef": 24, "parentId": 2, "parentPos": 4.0 + }], + ["AddRecord", "_grist_Views_section_field", 5, { + "colRef": 24, "parentId": 3, "parentPos": 5.0 }], ["BulkUpdateRecord", "Schools", [1, 2, 3], {"grist_Transform": ["New York", "Colombia", "New York"]}], @@ -122,7 +126,7 @@ class TestUserActions(test_engine.EngineTestCase): out_actions = self.remove_column('Schools', 'grist_Transform') self.assertPartialOutActions(out_actions, { "stored": [ - ["RemoveRecord", "_grist_Views_section_field", 3], + ["BulkRemoveRecord", "_grist_Views_section_field", [4, 5]], ['RemoveRecord', '_grist_Tables_column', 24], ['RemoveColumn', 'Schools', 'grist_Transform'], ]}) @@ -205,10 +209,10 @@ class TestUserActions(test_engine.EngineTestCase): Column(25, "C", "Any", isFormula=True, formula="", summarySourceCol=0), ]) new_view = View(1, sections=[ - Section(2, parentKey="record", tableRef=2, fields=[ - Field(4, colRef=23), - Field(5, colRef=24), - Field(6, colRef=25), + Section(3, parentKey="record", tableRef=2, fields=[ + Field(7, colRef=23), + Field(8, colRef=24), + Field(9, colRef=25), ]) ]) self.assertTables([self.starting_table, new_table]) @@ -223,10 +227,10 @@ class TestUserActions(test_engine.EngineTestCase): Column(29, "C", "Any", isFormula=True, formula="", summarySourceCol=0), ]) new_view.sections.append( - Section(4, parentKey="record", tableRef=3, fields=[ - Field(10, colRef=27), - Field(11, colRef=28), - Field(12, colRef=29), + Section(6, parentKey="record", tableRef=3, fields=[ + Field(16, colRef=27), + Field(17, colRef=28), + Field(18, colRef=29), ]) ) # Check that we have a new table, only the new view; and a new section. @@ -256,8 +260,8 @@ class TestUserActions(test_engine.EngineTestCase): Column(35, "count", "Int", isFormula=True, formula="len($group)", summarySourceCol=0), ]) self.assertTables([self.starting_table, new_table, new_table2, new_table3, summary_table]) - new_view.sections.append(Section(7, parentKey="record", tableRef=5, fields=[ - Field(17, colRef=35) + new_view.sections.append(Section(10, parentKey="record", tableRef=5, fields=[ + Field(26, colRef=35) ])) self.assertViews([new_view]) @@ -311,26 +315,26 @@ class TestUserActions(test_engine.EngineTestCase): ]), ]), View(2, sections=[ - Section(3, parentKey="detail", tableRef=1, fields=[ - Field(7, colRef=2), - Field(8, colRef=3), - Field(9, colRef=4), + Section(4, parentKey="detail", tableRef=1, fields=[ + Field(10, colRef=2), + Field(11, colRef=3), + Field(12, colRef=4), ]), - Section(5, parentKey="record", tableRef=2, fields=[ - Field(13, colRef=5), - Field(14, colRef=7), - Field(15, colRef=8), + Section(6, parentKey="record", tableRef=2, fields=[ + Field(16, colRef=5), + Field(17, colRef=7), + Field(18, colRef=8), ]), - Section(8, parentKey='record', tableRef=3, fields=[ - Field(21, colRef=10), - Field(22, colRef=11), - Field(23, colRef=12), + Section(10, parentKey='record', tableRef=3, fields=[ + Field(27, colRef=10), + Field(28, colRef=11), + Field(29, colRef=12), ]), ]), View(3, sections=[ - Section(6, parentKey="chart", tableRef=1, fields=[ - Field(16, colRef=2), - Field(17, colRef=3), + Section(7, parentKey="chart", tableRef=1, fields=[ + Field(19, colRef=2), + Field(20, colRef=3), ]), ]) ]) @@ -468,10 +472,10 @@ class TestUserActions(test_engine.EngineTestCase): {'title': 'Z'}]) self.assertTableData('_grist_Tables', cols="subset", data=[ - ['id', 'tableId', 'primaryViewId', 'rawViewSectionRef'], - [1, 'Z', 1, 2], - [2, 'Z_summary_state', 0, 4], - [3, 'Table1', 0, 7], + ['id', 'tableId', 'primaryViewId', 'rawViewSectionRef', 'recordCardViewSectionRef'], + [1, 'Z', 1, 2, 3], + [2, 'Z_summary_state', 0, 5, 0], + [3, 'Table1', 0, 8, 9], ]) self.assertTableData('_grist_Views', cols="subset", data=[ ['id', 'name'], @@ -485,11 +489,11 @@ class TestUserActions(test_engine.EngineTestCase): {'id': 'city', 'type': 'Text'}, ]]) self.assertTableData('_grist_Tables', cols="subset", data=[ - ['id', 'tableId', 'primaryViewId', 'rawViewSectionRef'], - [1, 'Z', 1, 2], - [2, 'Z_summary_state', 0, 4], - [3, 'Table1', 0, 7], - [4, 'Stations', 4, 10], + ['id', 'tableId', 'primaryViewId', 'rawViewSectionRef', 'recordCardViewSectionRef'], + [1, 'Z', 1, 2, 3], + [2, 'Z_summary_state', 0, 5, 0], + [3, 'Table1', 0, 8, 9], + [4, 'Stations', 4, 12, 13], ]) self.assertTableData('_grist_Views', cols="subset", data=[ ['id', 'name'], @@ -542,32 +546,32 @@ class TestUserActions(test_engine.EngineTestCase): ]), ]), View(2, sections=[ - Section(3, parentKey="detail", tableRef=1, fields=[ - Field(7, colRef=2), - Field(8, colRef=3), - Field(9, colRef=4), + Section(4, parentKey="detail", tableRef=1, fields=[ + Field(10, colRef=2), + Field(11, colRef=3), + Field(12, colRef=4), ]), - Section(5, parentKey="record", tableRef=2, fields=[ - Field(13, colRef=5), - Field(14, colRef=7), - Field(15, colRef=8), + Section(6, parentKey="record", tableRef=2, fields=[ + Field(16, colRef=5), + Field(17, colRef=7), + Field(18, colRef=8), ]), - Section(8, parentKey='record', tableRef=3, fields=[ - Field(21, colRef=10), - Field(22, colRef=11), - Field(23, colRef=12), + Section(10, parentKey='record', tableRef=3, fields=[ + Field(27, colRef=10), + Field(28, colRef=11), + Field(29, colRef=12), ]), ]), View(3, sections=[ - Section(6, parentKey="chart", tableRef=1, fields=[ - Field(16, colRef=2), - Field(17, colRef=3), + Section(7, parentKey="chart", tableRef=1, fields=[ + Field(19, colRef=2), + Field(20, colRef=3), ]), ]) ]) # Remove a couple of sections. Ensure their fields get removed. - self.apply_user_action(['BulkRemoveRecord', '_grist_Views_section', [5, 8]]) + self.apply_user_action(['BulkRemoveRecord', '_grist_Views_section', [6, 10]]) self.assertViews([ View(1, sections=[ @@ -578,16 +582,16 @@ class TestUserActions(test_engine.EngineTestCase): ]), ]), View(2, sections=[ - Section(3, parentKey="detail", tableRef=1, fields=[ - Field(7, colRef=2), - Field(8, colRef=3), - Field(9, colRef=4), + Section(4, parentKey="detail", tableRef=1, fields=[ + Field(10, colRef=2), + Field(11, colRef=3), + Field(12, colRef=4), ]) ]), View(3, sections=[ - Section(6, parentKey="chart", tableRef=1, fields=[ - Field(16, colRef=2), - Field(17, colRef=3), + Section(7, parentKey="chart", tableRef=1, fields=[ + Field(19, colRef=2), + Field(20, colRef=3), ]), ]) ]) @@ -613,8 +617,8 @@ class TestUserActions(test_engine.EngineTestCase): self.assertEqual(count_calls[0], 0) # Do a schema action to ensure it gets called: this causes a table rename. - # 7 is id of raw view section for the Tabl1 table - self.apply_user_action(['UpdateRecord', '_grist_Views_section', 7, {'title': 'C'}]) + # 8 is id of raw view section for the Table1 table + self.apply_user_action(['UpdateRecord', '_grist_Views_section', 8, {'title': 'C'}]) self.assertEqual(count_calls[0], 1) self.assertTableData('_grist_Tables', cols="subset", data=[ @@ -1403,6 +1407,7 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "parentId", "tableRef"], [1, 1, 2], [2, 0, 2], # the raw view section + [3, 0, 2], # the record card view section ]) self.assertTableData('_grist_Views_section_field', cols="subset", data=[ ["id", "parentId"], @@ -1414,6 +1419,11 @@ class TestUserActions(test_engine.EngineTestCase): [4, 2], [5, 2], [6, 2], + + # the record card view section + [7, 3], + [8, 3], + [9, 3], ]) # Test that the records cannot be removed by normal user actions @@ -1433,6 +1443,7 @@ class TestUserActions(test_engine.EngineTestCase): ["id", "parentId", "tableRef"], [1, 1, 2], [2, 0, 2], # the raw view section + [3, 0, 2], # the record card view section ]) self.assertTableData('_grist_Views_section_field', cols="subset", data=[ ["id", "parentId"], @@ -1444,6 +1455,45 @@ class TestUserActions(test_engine.EngineTestCase): [4, 2], [5, 2], [6, 2], + + # the record card view section + [7, 3], + [8, 3], + [9, 3], + ]) + + def test_record_card_view_section_restrictions(self): + self.load_sample(self.sample) + self.apply_user_action(["AddEmptyTable", None]) + + # Check that record card view sections cannot be removed by normal user actions. + with self.assertRaisesRegex(ValueError, "Cannot remove record card view section$"): + self.apply_user_action(["RemoveRecord", '_grist_Views_section', 3]) + + # Check that most of their column values can't be changed. + with self.assertRaisesRegex(ValueError, "Cannot modify record card view section$"): + self.apply_user_action(["UpdateRecord", '_grist_Views_section', 3, {"parentId": 1}]) + with self.assertRaisesRegex(ValueError, "Cannot modify record card view section fields$"): + self.apply_user_action(["UpdateRecord", '_grist_Views_section_field', 9, {"parentId": 1}]) + + # Make sure nothing got removed or updated. + self.assertTableData('_grist_Views_section', cols="subset", data=[ + ["id", "parentId", "tableRef"], + [1, 1, 2], + [2, 0, 2], + [3, 0, 2], + ]) + self.assertTableData('_grist_Views_section_field', cols="subset", data=[ + ["id", "parentId"], + [1, 1], + [2, 1], + [3, 1], + [4, 2], + [5, 2], + [6, 2], + [7, 3], + [8, 3], + [9, 3], ]) def test_update_current_time(self): diff --git a/sandbox/grist/testscript.json b/sandbox/grist/testscript.json index 5c5b7cf5..65774ca3 100644 --- a/sandbox/grist/testscript.json +++ b/sandbox/grist/testscript.json @@ -918,7 +918,12 @@ // Raw data widget ["AddRecord", "_grist_Views_section", 2, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 4, "title": ""}], ["AddRecord", "_grist_Views_section_field", 2, {"colRef": 34, "parentId": 2, "parentPos": 2.0}], - ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2}], + + // Record card widget + ["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}], + ["AddRecord", "_grist_Views_section_field", 3, {"colRef": 34, "parentId": 3, "parentPos": 3.0}], + + ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2, "recordCardViewSectionRef": 3}], // Actions generated from AddColumn. ["AddColumn", "Bar", "world", @@ -927,9 +932,10 @@ {"colId": "world", "parentPos": 13.0, "formula": "rec.hello.upper()", "parentId": 4, "type": "Text", "isFormula": true, "label": "world", "widgetOptions": ""}], - ["AddRecord", "_grist_Views_section_field", 3, {"colRef": 35, "parentId": 2, "parentPos": 3.0}] + ["AddRecord", "_grist_Views_section_field", 4, {"colRef": 35, "parentId": 2, "parentPos": 4.0}], + ["AddRecord", "_grist_Views_section_field", 5, {"colRef": 35, "parentId": 3, "parentPos": 5.0}] ], - "direct": [true, true, true, true, + "direct": [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true], "undo": [ @@ -943,10 +949,13 @@ ["RemoveRecord", "_grist_Views_section_field", 1], ["RemoveRecord", "_grist_Views_section", 2], ["RemoveRecord", "_grist_Views_section_field", 2], - ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0}], + ["RemoveRecord", "_grist_Views_section", 3], + ["RemoveRecord", "_grist_Views_section_field", 3], + ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}], ["RemoveColumn", "Bar", "world"], ["RemoveRecord", "_grist_Tables_column", 35], - ["RemoveRecord", "_grist_Views_section_field", 3] + ["RemoveRecord", "_grist_Views_section_field", 4], + ["RemoveRecord", "_grist_Views_section_field", 5] ], "retValue": [ { @@ -1257,15 +1266,16 @@ {"parentId": [1,1], "colRef": [31,32], "parentPos": [1.0,2.0]}], ["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]}], - ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2}], - ["BulkRemoveRecord", "_grist_Views_section_field", [1, 3]], + ["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}], + ["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}], + ["BulkRemoveRecord", "_grist_Views_section_field", [1, 3, 5]], ["RemoveRecord", "_grist_Tables_column", 31], ["RemoveColumn", "ViewTest", "hello"] ], "direct": [true, true, true, true, true, true, true, true, true, - true, true, - true, true, true], + true, true, true, true, true, true, true], "undo": [ ["RemoveTable", "ViewTest"], ["RemoveRecord", "_grist_Tables", 4], @@ -1277,8 +1287,11 @@ ["BulkRemoveRecord", "_grist_Views_section_field", [1,2]], ["RemoveRecord", "_grist_Views_section", 2], ["BulkRemoveRecord", "_grist_Views_section_field", [3, 4]], - ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0}], - ["BulkAddRecord", "_grist_Views_section_field", [1, 3], {"colRef": [31, 31], "parentId": [1, 2], "parentPos": [1.0, 3.0]}], + ["RemoveRecord", "_grist_Views_section", 3], + ["BulkRemoveRecord", "_grist_Views_section_field", [5, 6]], + ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}], + ["BulkAddRecord", "_grist_Views_section_field", [1, 3, 5], + {"colRef": [31, 31, 31], "parentId": [1, 2, 3], "parentPos": [1.0, 3.0, 5.0]}], ["AddRecord", "_grist_Tables_column", 31, {"colId": "hello", "parentPos": 9.0, "parentId": 4, "type": "Text" @@ -2199,7 +2212,8 @@ {"tableRef": 4, "defaultWidth": 100, "borderWidth": 1, "parentId": 1, "parentKey": "record", "sortColRefs": "[]", "title": ""}], ["AddRecord", "_grist_Views_section", 2, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 4, "title": ""}], - ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2}], + ["AddRecord", "_grist_Views_section", 3, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 4, "title": ""}], + ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2, "recordCardViewSectionRef": 3}], ["AddTable", "Bar", [ {"id": "manualSort", "formula": "", "isFormula": false, "type": "ManualSortPos"}, {"isFormula": false, "formula": "", "type": "Text", "id": "hello"}, @@ -2223,21 +2237,23 @@ {"type": "raw_data", "name": "Bar"}], ["AddRecord", "_grist_TabBar", 2, {"tabPos": 2.0, "viewRef": 2}], ["AddRecord", "_grist_Pages", 2, {"pagePos": 2.0, "viewRef": 2, "indentation": 0}], - ["AddRecord", "_grist_Views_section", 3, + ["AddRecord", "_grist_Views_section", 4, {"tableRef": 5, "defaultWidth": 100, "borderWidth": 1, "parentId": 2, "parentKey": "record", "sortColRefs": "[]", "title": ""}], ["BulkAddRecord", "_grist_Views_section_field", [1,2,3], - {"parentId": [3,3,3], "colRef": [32,33,34], "parentPos": [1.0,2.0,3.0]}], - ["AddRecord", "_grist_Views_section", 4, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 5, "title": ""}], - ["BulkAddRecord", "_grist_Views_section_field", [4, 5, 6], {"colRef": [32, 33, 34], "parentId": [4, 4, 4], "parentPos": [4.0, 5.0, 6.0]}], - ["UpdateRecord", "_grist_Tables", 5, {"primaryViewId": 2, "rawViewSectionRef": 4}], + {"parentId": [4,4,4], "colRef": [32,33,34], "parentPos": [1.0,2.0,3.0]}], + ["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]}], + ["AddRecord", "_grist_Views_section", 6, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "single", "tableRef": 5, "title": ""}], + ["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}], ["AddRecord", "Bar", 1, {"foo": 0, "hello": "a", "manualSort": 1.0}], ["AddRecord", "Bar", 2, {"foo": 1, "hello": "b", "manualSort": 2.0}], ["AddRecord", "Bar", 3, {"foo": 1, "hello": "c", "manualSort": 3.0}], ["BulkUpdateRecord", "Bar", [1, 2, 3], {"world": ["A", "B", "C"]}] ], "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, false], "undo": [ @@ -2249,18 +2265,21 @@ ["RemoveRecord", "_grist_Pages", 1], ["RemoveRecord", "_grist_Views_section", 1], ["RemoveRecord", "_grist_Views_section", 2], - ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0}], + ["RemoveRecord", "_grist_Views_section", 3], + ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}], ["RemoveTable", "Bar"], ["RemoveRecord", "_grist_Tables", 5], ["BulkRemoveRecord", "_grist_Tables_column", [31,32,33,34]], ["RemoveRecord", "_grist_Views", 2], ["RemoveRecord", "_grist_TabBar", 2], ["RemoveRecord", "_grist_Pages", 2], - ["RemoveRecord", "_grist_Views_section", 3], - ["BulkRemoveRecord", "_grist_Views_section_field", [1,2,3]], ["RemoveRecord", "_grist_Views_section", 4], + ["BulkRemoveRecord", "_grist_Views_section_field", [1,2,3]], + ["RemoveRecord", "_grist_Views_section", 5], ["BulkRemoveRecord", "_grist_Views_section_field", [4, 5, 6]], - ["UpdateRecord", "_grist_Tables", 5, {"primaryViewId": 0, "rawViewSectionRef": 0}], + ["RemoveRecord", "_grist_Views_section", 6], + ["BulkRemoveRecord", "_grist_Views_section_field", [7, 8, 9]], + ["UpdateRecord", "_grist_Tables", 5, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}], ["RemoveRecord", "Bar", 1], ["RemoveRecord", "Bar", 2], ["RemoveRecord", "Bar", 3] @@ -2281,7 +2300,7 @@ "id": 5, "columns": ["hello", "world", "foo"], "views": [ - { "sections": [ 3 ], "id": 2 } + { "sections": [ 4 ], "id": 2 } ] }, // AddRecord retValues @@ -2333,10 +2352,12 @@ "parentId": 1, "parentKey": "record", "sortColRefs": "[]", "title": ""}], // Raw data widget ["AddRecord", "_grist_Views_section", 2, {"borderWidth": 1, "defaultWidth": 100, "parentKey": "record", "tableRef": 4, "title": ""}], + // Record card widget + ["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. - ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2}] + ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 1, "rawViewSectionRef": 2, "recordCardViewSectionRef": 3}] ], - "direct": [true, true, true, true, true, true, true, true, true], + "direct": [true, true, true, true, true, true, true, true, true, true], "undo": [ ["RemoveTable", "Foo"], ["RemoveRecord", "_grist_Tables", 4], @@ -2346,7 +2367,8 @@ ["RemoveRecord", "_grist_Pages", 1], ["RemoveRecord", "_grist_Views_section", 1], ["RemoveRecord", "_grist_Views_section", 2], - ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0}] + ["RemoveRecord", "_grist_Views_section", 3], + ["UpdateRecord", "_grist_Tables", 4, {"primaryViewId": 0, "rawViewSectionRef": 0, "recordCardViewSectionRef": 0}] ] } }], @@ -2359,7 +2381,8 @@ "USER_ACTION": ["RemoveTable", "Foo"], "ACTIONS": { "stored": [ - ["BulkRemoveRecord", "_grist_Views_section", [1, 2]], + ["BulkRemoveRecord", "_grist_Views_section", [1, 2, 3]], + ["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 0}], ["UpdateRecord", "_grist_Tables", 4, {"rawViewSectionRef": 0}], ["RemoveRecord", "_grist_TabBar", 1], ["RemoveRecord", "_grist_Pages", 1], @@ -2369,11 +2392,12 @@ ["RemoveRecord", "_grist_Tables", 4], ["RemoveTable", "Foo"] ], - "direct": [true, true, true, true, true, true, true, true, true], + "direct": [true, true, true, true, true, true, true, true, true, true], "undo": [ - ["BulkAddRecord", "_grist_Views_section", [1, 2], - {"borderWidth": [1, 1], "defaultWidth": [100, 100], "parentId": [1, 0], - "parentKey": ["record", "record"], "sortColRefs": ["[]", ""], "tableRef": [4, 4]}], + ["BulkAddRecord", "_grist_Views_section", [1, 2, 3], + {"borderWidth": [1, 1, 1], "defaultWidth": [100, 100, 100], "parentId": [1, 0, 0], + "parentKey": ["record", "record", "single"], "sortColRefs": ["[]", "", ""], "tableRef": [4, 4, 4]}], + ["UpdateRecord", "_grist_Tables", 4, {"recordCardViewSectionRef": 3}], ["UpdateRecord", "_grist_Tables", 4, {"rawViewSectionRef": 2}], ["AddRecord", "_grist_TabBar", 1, {"tabPos": 1.0, "viewRef": 1}], ["AddRecord", "_grist_Pages", 1, {"pagePos": 1.0, "viewRef": 1}], diff --git a/sandbox/grist/useractions.py b/sandbox/grist/useractions.py index 21f4c31e..5acc2c55 100644 --- a/sandbox/grist/useractions.py +++ b/sandbox/grist/useractions.py @@ -479,6 +479,24 @@ class UserActions(object): ): raise ValueError("Cannot modify raw view section fields") + # Prevent modifying record card widgets and their fields. + if ( + table_id == "_grist_Views_section" + and any(rec.isRecordCard for i, rec in self._bulk_action_iter(table_id, row_ids)) + ): + allowed_fields = {"layoutSpec", "options", "theme"} + if not set(column_values) <= allowed_fields: + raise ValueError("Cannot modify record card view section") + + if ( + table_id == "_grist_Views_section_field" + and any(rec.parentId.isRecordCard for i, rec in self._bulk_action_iter(table_id, row_ids)) + and not set(column_values) <= { + "displayCol", "parentPos", "rules", "visibleCol", "widgetOptions" + } + ): + raise ValueError("Cannot modify record card view section fields") + # If any extra actions were generated (e.g. to adjust positions), apply them. for a in extra_actions: self._do_doc_action(a) @@ -1300,13 +1318,15 @@ class UserActions(object): def _removeViewSectionRecords(self, table_id, row_ids): """ Remove view sections, including their fields. - Raises an error if trying to remove a table's rawViewSectionRef. + Raises an error if trying to remove a table's rawViewSectionRef or recordCardViewSectionRef. To bypass that check, call _doRemoveViewSectionRecords. """ recs = [rec for i, rec in self._bulk_action_iter(table_id, row_ids)] for rec in recs: if rec.isRaw: raise ValueError("Cannot remove raw view section") + if rec.isRecordCard: + raise ValueError("Cannot remove record card view section") self._doRemoveViewSectionRecords(recs) def _doRemoveViewSectionRecords(self, recs): @@ -1355,16 +1375,35 @@ class UserActions(object): ret = self.doAddColumn(table_id, col_id, col_info) - if not transform and table_rec.rawViewSectionRef: - # Add a field for this column to the "raw_data" section for this table. - # TODO: the position of the inserted field or of the inserted column will often be - # bogus, since fields and columns are not the same. This requires better coordination - # with the client-side. - self._docmodel.insert( - table_rec.rawViewSectionRef.fields, - col_info.get('_position'), - colRef=ret['colRef'] - ) + if not transform: + if table_rec.rawViewSectionRef: + # Add a field for this column to the "raw_data" section for this table. + # TODO: the position of the inserted field or of the inserted column will often be + # bogus, since fields and columns are not the same. This requires better coordination + # with the client-side. + self._docmodel.insert( + table_rec.rawViewSectionRef.fields, + col_info.get('_position'), + colRef=ret['colRef'] + ) + + if table_rec.recordCardViewSectionRef: + # If the record card section or one of its fields hasn't yet been modified, + # add a field for this column. + section = table_rec.recordCardViewSectionRef + modified = ( + section.layoutSpec or + section.options or + section.rules or + section.theme or + any(f.widgetOptions for f in section.fields) + ) + if not modified: + self._docmodel.insert( + table_rec.recordCardViewSectionRef.fields, + col_info.get('_position'), + colRef=ret['colRef'] + ) return ret @@ -1807,7 +1846,8 @@ class UserActions(object): columns, manual_sort=True, primary_view=True, - raw_section=True) + raw_section=True, + record_card_section=True) @useraction @@ -1821,12 +1861,14 @@ class UserActions(object): columns, manual_sort=True, primary_view=False, - raw_section=True + raw_section=True, + record_card_section=True ) def doAddTable(self, table_id, columns, manual_sort=False, primary_view=False, - raw_section=False, summarySourceTableRef=0): + raw_section=False, record_card_section=False, + summarySourceTableRef=0): """ Add the given table with columns with or without additional views. """ @@ -1887,10 +1929,20 @@ class UserActions(object): table_title if not summarySourceTableRef else "" ) - if primary_view or raw_section: + if record_card_section: + record_card_section = self.create_plain_view_section( + result["id"], + table_id, + self._docmodel.view_sections, + "single", + "" + ) + + if primary_view or raw_section or record_card_section: self.UpdateRecord('_grist_Tables', result["id"], { 'primaryViewId': primary_view["id"] if primary_view else 0, 'rawViewSectionRef': raw_section.id if raw_section else 0, + 'recordCardViewSectionRef': record_card_section.id if record_card_section else 0, }) return result @@ -1924,6 +1976,7 @@ class UserActions(object): # Copy the columns from the raw view section to a new table. raw_section = existing_table.rawViewSectionRef + record_card_section = existing_table.recordCardViewSectionRef raw_section_cols = [f.colRef for f in raw_section.fields] col_info = [summary.make_col_info(col=c) for c in raw_section_cols] columns = [summary.get_colinfo_dict(ci, with_id=True) for ci in col_info] @@ -1933,13 +1986,19 @@ class UserActions(object): manual_sort=True, primary_view=False, raw_section=True, + record_card_section=True, ) new_table_id = result['table_id'] - new_raw_section = self._docmodel.get_table_rec(new_table_id).rawViewSectionRef - - # Copy view section options to the new raw view section. - self._docmodel.update([new_raw_section], options=raw_section.options) + new_table = self._docmodel.get_table_rec(new_table_id) + new_raw_section = new_table.rawViewSectionRef + new_record_card_section = new_table.recordCardViewSectionRef + + # Copy view section options to the new raw and record card view sections. + self._docmodel.update( + [new_raw_section, new_record_card_section], + options=[raw_section.options, record_card_section.options] + ) old_to_new_col_refs = {} for existing_field, new_field in zip(raw_section.fields, new_raw_section.fields): diff --git a/test/fixtures/docs/Hello.grist b/test/fixtures/docs/Hello.grist index 6effd2baa0f64e59e96002cab3ccb3951e96ff0f..20ba90376902e8b4216ddacb8a393e4ef238b184 100644 GIT binary patch delta 303 zcmZp8z})bFd4jZ{I0FNN4G_bC^h6zFM)8dawc?B#o9~Hhuy7n?VEVv#kn#CuL6*&o zTunM`?BeR`j4k4mf69L2(@{_=N=?o$N^wpsN(r01aD~F=_j2NlEbkbY-vKo|VHOf# zUE|Jts9~@{wQ%C4OcO2IjZSx0sJJZ(&}@Jej$Lxs*ALIh@&p*@9VvS%R4Z zXwoAle?ES021Z6k4qhN+U}j9GXQLZ BIp6>Q delta 165 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

3l7S6WUZzzmXRhz@?7 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

(-*!!Xy;o6)!DNm7f>lk(i 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

^P)R5YfOeFmoDHX|o0*W< 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¬V=HvR({0Jt3GK5uVdj%&JrHX+ABxInaW*Z+--S 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%0ATd$i^&vTLmgze#*QRMf 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 literal 0 HcmV?d00001 diff --git a/test/nbrowser/LinkingErrors.ts b/test/nbrowser/LinkingErrors.ts index 5d5d4aa6..ebbdee29 100644 --- a/test/nbrowser/LinkingErrors.ts +++ b/test/nbrowser/LinkingErrors.ts @@ -76,11 +76,11 @@ describe("LinkingErrors", function() { const planetsTable = tables.filterRecords({tableId: 'Planets'})[0]; assert.isOk(planetsTable); const planetsSections = sections.filterRecords({tableRef: planetsTable.id}); - assert.lengthOf(planetsSections, 3); - assert.equal(planetsSections[0].parentId, planetsSections[2].parentId); - assert.deepEqual(planetsSections.map(s => s.linkTargetColRef), [0, 0, 0]); - assert.deepEqual(planetsSections.map(s => s.linkSrcSectionRef), [0, 0, 0]); - assert.deepEqual(planetsSections.map(s => s.linkSrcColRef), [0, 0, 0]); + assert.lengthOf(planetsSections, 4); + assert.equal(planetsSections[0].parentId, planetsSections[3].parentId); + assert.deepEqual(planetsSections.map(s => s.linkTargetColRef), [0, 0, 0, 0]); + assert.deepEqual(planetsSections.map(s => s.linkSrcSectionRef), [0, 0, 0, 0]); + assert.deepEqual(planetsSections.map(s => s.linkSrcColRef), [0, 0, 0, 0]); // Switch to another page and back and check that there are no errors. await gu.getPageItem('Moons').click(); @@ -151,8 +151,8 @@ describe("LinkingErrors", function() { ['AddEmptyTable', null], ['UpdateRecord', '_grist_Tables_column', 6, {type: 'Ref:Table1'}], ['CreateViewSection', 2, 1, 'record', null, null], - ['UpdateRecord', '_grist_Views_section', 3, {linkSrcSectionRef: 1, linkSrcColRef: 0, linkTargetColRef: 0}], - ['UpdateRecord', '_grist_Views_section', 6, {linkSrcSectionRef: 1, linkSrcColRef: 0, linkTargetColRef: 6}], + ['UpdateRecord', '_grist_Views_section', 4, {linkSrcSectionRef: 1, linkSrcColRef: 0, linkTargetColRef: 0}], + ['UpdateRecord', '_grist_Views_section', 8, {linkSrcSectionRef: 1, linkSrcColRef: 0, linkTargetColRef: 6}], [ 'UpdateRecord', '_grist_Views', diff --git a/test/nbrowser/RawData.ts b/test/nbrowser/RawData.ts index 091c90f2..815d5664 100644 --- a/test/nbrowser/RawData.ts +++ b/test/nbrowser/RawData.ts @@ -374,7 +374,7 @@ describe('RawData', function () { // The last table should have disabled remove button. await openMenu(allTables[0]); - assert.isTrue(await driver.find('.test-raw-data-menu-remove.disabled').isDisplayed()); + assert.isTrue(await driver.find('.test-raw-data-menu-remove-table.disabled').isDisplayed()); await gu.sendKeys(Key.ESCAPE); }); @@ -542,8 +542,8 @@ describe('RawData', function () { await gu.selectSectionByTitle("COUNTRY Card List"); await gu.getDetailCell('Code', 1).click(); await gu.addNewSection(/Chart/, /CountryLanguage/); - // s19 is the new section id, we also strip row/column. - let chartLink = replaceAnchor(await gu.getAnchor(), {s: '19', a: '2'}); + // s22 is the new section id, we also strip row/column. + let chartLink = replaceAnchor(await gu.getAnchor(), {s: '22', a: '2'}); await gu.getPageItem('City').click(); chartLink = (await driver.getCurrentUrl()) + '#' + chartLink.split('#')[1]; await waitForAnchorPopup(chartLink); @@ -623,7 +623,7 @@ async function clickDuplicateTable() { } async function clickRemove() { - await driver.find('.test-raw-data-menu-remove').click(); + await driver.find('.test-raw-data-menu-remove-table').click(); } async function removeRawTable(tableId: string) { @@ -681,7 +681,7 @@ async function waitForRawData() { async function isRemovable(tableId: string){ await openMenu(tableId); - const disabledItems = await driver.findAll('.test-raw-data-menu-remove.disabled'); + const disabledItems = await driver.findAll('.test-raw-data-menu-remove-table.disabled'); await gu.sendKeys(Key.ESCAPE); return disabledItems.length === 0; } diff --git a/test/nbrowser/ReferenceColumns.ts b/test/nbrowser/ReferenceColumns.ts index 485019f6..bbeafd06 100644 --- a/test/nbrowser/ReferenceColumns.ts +++ b/test/nbrowser/ReferenceColumns.ts @@ -164,7 +164,8 @@ describe('ReferenceColumns', function() { it('should open to correct item selected, and leave it unchanged on Enter', async function() { const checkRefCell = stackWrapFunc(async (col: string, rowNum: number, expValue: string) => { // Click cell and open for editing. - const cell = await gu.getCell({section: 'References', col, rowNum}).doClick(); + const cell = await gu.getCell({section: 'References', col, rowNum}) + .find('.test-ref-text').doClick(); assert.equal(await cell.getText(), expValue); await driver.sendKeys(Key.ENTER); // Wait for expected value to appear in the list; check that it's selected. @@ -453,7 +454,8 @@ describe('ReferenceColumns', function() { }); it('should update choices as user types into textbox', async function() { - let cell = await gu.getCell({section: 'References', col: 'School', rowNum: 1}).doClick(); + let cell = await gu.getCell({section: 'References', col: 'School', rowNum: 1}) + .find('.test-ref-text').doClick(); assert.equal(await cell.getText(), 'TECHNOLOGY, ARTS AND SCIENCES STUDIO'); await driver.sendKeys(Key.ENTER); assert.deepEqual(await getACOptions(3), [ @@ -493,7 +495,8 @@ describe('ReferenceColumns', function() { it('should highlight matching parts of items', async function() { await driver.sendKeys(Key.HOME); - let cell = await gu.getCell({section: 'References', col: 'Color', rowNum: 2}).doClick(); + let cell = await gu.getCell({section: 'References', col: 'Color', rowNum: 2}) + .find('.test-ref-text').doClick(); assert.equal(await cell.getText(), 'Red'); await driver.sendKeys(Key.ENTER); await driver.findWait('.test-ref-editor-item', 1000); @@ -505,7 +508,8 @@ describe('ReferenceColumns', function() { ['Re']); await driver.sendKeys(Key.ESCAPE); - cell = await gu.getCell({section: 'References', col: 'School', rowNum: 1}).doClick(); + cell = await gu.getCell({section: 'References', col: 'School', rowNum: 1}) + .find('.test-ref-text').doClick(); await driver.sendKeys('br tech'); assert.deepEqual( await driver.findContentWait('.test-ref-editor-item', /BROOKLYN TECH/, 1000).findAll('span', e => e.getText()), diff --git a/test/nbrowser/TypeChange.ntest.js b/test/nbrowser/TypeChange.ntest.js index 1f677d88..1e70bf1d 100644 --- a/test/nbrowser/TypeChange.ntest.js +++ b/test/nbrowser/TypeChange.ntest.js @@ -141,7 +141,7 @@ describe('TypeChange.ntest', function() { // Prepare new table and section await gu.actions.addNewSection('New', 'Table'); await gu.waitForServer(); - await $('.test-viewlayout-section-4').click(); + await $('.test-viewlayout-section-6').click(); await gu.addRecord(['green']); await gu.addRecord(['blue']); diff --git a/test/nbrowser/gristUtils.ts b/test/nbrowser/gristUtils.ts index 37172a1f..cfc574a3 100644 --- a/test/nbrowser/gristUtils.ts +++ b/test/nbrowser/gristUtils.ts @@ -1260,7 +1260,7 @@ export async function removeTable(tableId: string, options: {dismissTips?: boole const menus = await driver.findAll(".test-raw-data-table .test-raw-data-table-menu"); assert.equal(menus.length, tableIdList.length); await menus[tableIndex].click(); - await driver.find(".test-raw-data-menu-remove").click(); + await driver.find(".test-raw-data-menu-remove-table").click(); await driver.find(".test-modal-confirm").click(); await waitForServer(); } @@ -1521,8 +1521,9 @@ export async function openRawTable(tableId: string) { export async function renameRawTable(tableId: string, newName: string) { await driver.find(`.test-raw-data-table .test-raw-data-table-id-${tableId}`) .findClosest('.test-raw-data-table') - .find('.test-widget-title-text') + .find('.test-raw-data-table-menu') .click(); + await driver.find('.test-raw-data-menu-rename-table').click(); const input = await driver.find(".test-widget-title-table-name-input"); await input.doClear(); await input.click(); diff --git a/test/server/lib/DocApi.ts b/test/server/lib/DocApi.ts index 07798345..1075079c 100644 --- a/test/server/lib/DocApi.ts +++ b/test/server/lib/DocApi.ts @@ -982,6 +982,7 @@ function testDocApi() { "id": "Table1", "fields": { "rawViewSectionRef": 2, + "recordCardViewSectionRef": 3, "primaryViewId": 1, "onDemand": false, "summarySourceTable": 0, @@ -992,7 +993,8 @@ function testDocApi() { { "id": "Table2", "fields": { - "rawViewSectionRef": 4, + "rawViewSectionRef": 5, + "recordCardViewSectionRef": 6, "primaryViewId": 2, "onDemand": false, "summarySourceTable": 0, @@ -1002,7 +1004,8 @@ function testDocApi() { { "id": "Table3_Renamed", "fields": { - "rawViewSectionRef": 6, + "rawViewSectionRef": 8, + "recordCardViewSectionRef": 9, "primaryViewId": 3, "onDemand": false, "summarySourceTable": 0, @@ -1012,7 +1015,8 @@ function testDocApi() { { "id": "NewTable1", "fields": { - "rawViewSectionRef": 8, + "rawViewSectionRef": 11, + "recordCardViewSectionRef": 12, "primaryViewId": 4, "onDemand": false, "summarySourceTable": 0, @@ -1022,7 +1026,8 @@ function testDocApi() { { "id": "NewTable2", "fields": { - "rawViewSectionRef": 10, + "rawViewSectionRef": 14, + "recordCardViewSectionRef": 15, "primaryViewId": 5, "onDemand": false, "summarySourceTable": 0,