(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
This commit is contained in:
George Gevoian
2023-11-19 19:46:32 -05:00
parent 2eec48b685
commit caf830db08
53 changed files with 1261 additions and 456 deletions

View File

@@ -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',

View File

@@ -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;
}

View File

@@ -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()),

View File

@@ -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']);

View File

@@ -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();