From 642b2fe1003d7cbd3616276fe1fc6aaea6fb2586 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Fri, 27 Jan 2023 17:40:22 +0100 Subject: [PATCH 1/4] Be more accepting of select options --- packages/toolpad-components/src/Select.tsx | 5 +- .../integration/components/componentsDom.json | 69 +++++++++++++++---- test/integration/components/index.spec.ts | 20 +++++- 3 files changed, 78 insertions(+), 16 deletions(-) diff --git a/packages/toolpad-components/src/Select.tsx b/packages/toolpad-components/src/Select.tsx index cc8d407566c..2d54236efc5 100644 --- a/packages/toolpad-components/src/Select.tsx +++ b/packages/toolpad-components/src/Select.tsx @@ -32,10 +32,11 @@ function Select({ options, value, onChange, defaultValue, fullWidth, sx, ...rest {...rest} > {options.map((option) => { - const parsedOption: SelectOption = typeof option === 'string' ? { value: option } : option; + const parsedOption: SelectOption = + option && typeof option === 'object' ? option : { value: String(option) }; return ( - {parsedOption.label ?? parsedOption.value} + {String(parsedOption.label ?? parsedOption.value)} ); })} diff --git a/test/integration/components/componentsDom.json b/test/integration/components/componentsDom.json index 0fd4e346ea3..83bb60d042b 100644 --- a/test/integration/components/componentsDom.json +++ b/test/integration/components/componentsDom.json @@ -22,6 +22,22 @@ "parentProp": "children", "parentIndex": "a0" }, + "1m13ljr": { + "id": "1m13ljr", + "name": "codeComponent_eb03t9a", + "type": "element", + "props": {}, + "layout": {}, + "parentId": "rx23l68", + "attributes": { + "component": { + "type": "const", + "value": "codeComponent.eb03t9a" + } + }, + "parentProp": "children", + "parentIndex": "a0" + }, "2m53pfl": { "id": "2m53pfl", "name": "pageRow5", @@ -173,7 +189,7 @@ "attributes": { "component": { "type": "const", - "value": "Typography" + "value": "Text" } }, "parentProp": "children", @@ -240,6 +256,22 @@ "parentProp": "children", "parentIndex": "a0" }, + "rx23l68": { + "id": "rx23l68", + "name": "pageRow", + "type": "element", + "props": {}, + "layout": {}, + "parentId": "f703ps3", + "attributes": { + "component": { + "type": "const", + "value": "PageRow" + } + }, + "parentProp": "children", + "parentIndex": "a1V" + }, "vkowdut": { "id": "vkowdut", "name": "Application", @@ -281,8 +313,8 @@ "parentProp": "children", "parentIndex": "a0" }, - "rx23l68": { - "name": "pageRow", + "5c43usr": { + "name": "pageRow1", "props": {}, "attributes": { "component": { @@ -291,28 +323,41 @@ } }, "layout": {}, - "id": "rx23l68", + "id": "5c43usr", "type": "element", "parentId": "f703ps3", "parentProp": "children", - "parentIndex": "a1V" + "parentIndex": "a6" }, - "1m13ljr": { - "name": "codeComponent_eb03t9a", - "props": {}, + "6n23u2h": { + "name": "selectWithOptions", + "props": { + "options": { + "type": "jsExpression", + "value": "[\"one\", 2, { value: 3, label: \"three\" }, { value: 4 }, { noValue: \"foo\" }]\n" + }, + "label": { + "type": "const", + "value": "select with options" + }, + "fullWidth": { + "type": "const", + "value": true + } + }, "attributes": { "component": { "type": "const", - "value": "codeComponent.eb03t9a" + "value": "Select" } }, "layout": {}, - "id": "1m13ljr", + "id": "6n23u2h", "type": "element", - "parentId": "rx23l68", + "parentId": "5c43usr", "parentProp": "children", "parentIndex": "a0" } }, - "version": 2 + "version": 5 } diff --git a/test/integration/components/index.spec.ts b/test/integration/components/index.spec.ts index 8effcf1228a..b02a0bda8cb 100644 --- a/test/integration/components/index.spec.ts +++ b/test/integration/components/index.spec.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import { ToolpadEditor } from '../../models/ToolpadEditor'; import { ToolpadRuntime } from '../../models/ToolpadRuntime'; -import { FrameLocator, Page, test } from '../../playwright/test'; +import { FrameLocator, Page, test, expect } from '../../playwright/test'; import { readJsonFile } from '../../utils/fs'; import generateId from '../../utils/generateId'; @@ -13,9 +13,17 @@ async function waitForComponents(page: Page | FrameLocator) { await page.locator('label:has-text("foo textfield")').waitFor({ state: 'visible' }); await page.locator('text="foo typography"').waitFor({ state: 'visible' }); await page.locator('label:has-text("foo select")').waitFor({ state: 'visible' }); + const optionsSelect = page.getByRole('button', { name: /select with options/ }); + await expect(optionsSelect).toBeVisible(); + await optionsSelect.click(); + await expect(page.getByRole('option', { name: 'one' })).toBeVisible(); + await expect(page.getByRole('option', { name: '2' })).toBeVisible(); + await expect(page.getByRole('option', { name: 'three' })).toBeVisible(); + await expect(page.getByRole('option', { name: '4' })).toBeVisible(); + await expect(page.getByRole('option', { name: 'undefined' })).toBeVisible(); } -test('components', async ({ page, browserName, api }) => { +test('components runtime', async ({ page, api }) => { const dom = await readJsonFile(path.resolve(__dirname, './componentsDom.json')); const app = await api.mutation.createApp(`App ${generateId()}`, { @@ -26,6 +34,14 @@ test('components', async ({ page, browserName, api }) => { await runtimeModel.gotoPage(app.id, 'components'); await waitForComponents(page); +}); + +test('components editor', async ({ page, browserName, api }) => { + const dom = await readJsonFile(path.resolve(__dirname, './componentsDom.json')); + + const app = await api.mutation.createApp(`App ${generateId()}`, { + from: { kind: 'dom', dom }, + }); const editorModel = new ToolpadEditor(page, browserName); editorModel.goto(app.id); From 6a157bac575ced2443d93bdbbd9da717e9ecdb0f Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Sun, 29 Jan 2023 13:16:14 +0100 Subject: [PATCH 2/4] fix test --- packages/toolpad-components/src/Select.tsx | 6 ++-- test/integration/components/index.spec.ts | 39 ++++++++++++---------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/packages/toolpad-components/src/Select.tsx b/packages/toolpad-components/src/Select.tsx index 2d54236efc5..748f1daee7a 100644 --- a/packages/toolpad-components/src/Select.tsx +++ b/packages/toolpad-components/src/Select.tsx @@ -22,6 +22,8 @@ function Select({ options, value, onChange, defaultValue, fullWidth, sx, ...rest [onChange], ); + const id = React.useId(); + return ( - {options.map((option) => { + {options.map((option, i) => { const parsedOption: SelectOption = option && typeof option === 'object' ? option : { value: String(option) }; return ( - + {String(parsedOption.label ?? parsedOption.value)} ); diff --git a/test/integration/components/index.spec.ts b/test/integration/components/index.spec.ts index b02a0bda8cb..23e19249e6a 100644 --- a/test/integration/components/index.spec.ts +++ b/test/integration/components/index.spec.ts @@ -2,25 +2,30 @@ import * as path from 'path'; import { ToolpadEditor } from '../../models/ToolpadEditor'; import { ToolpadRuntime } from '../../models/ToolpadRuntime'; import { FrameLocator, Page, test, expect } from '../../playwright/test'; +import clickCenter from '../../utils/clickCenter'; import { readJsonFile } from '../../utils/fs'; import generateId from '../../utils/generateId'; -async function waitForComponents(page: Page | FrameLocator) { - await page.locator('text="foo button"').waitFor({ state: 'visible' }); - await page.locator('img[alt="foo image"]').waitFor({ state: 'attached' }); - await page.locator('text="foo datagrid column"').waitFor({ state: 'visible' }); - await page.locator('text="custom component 1"').waitFor({ state: 'visible' }); - await page.locator('label:has-text("foo textfield")').waitFor({ state: 'visible' }); - await page.locator('text="foo typography"').waitFor({ state: 'visible' }); - await page.locator('label:has-text("foo select")').waitFor({ state: 'visible' }); - const optionsSelect = page.getByRole('button', { name: /select with options/ }); - await expect(optionsSelect).toBeVisible(); - await optionsSelect.click(); - await expect(page.getByRole('option', { name: 'one' })).toBeVisible(); - await expect(page.getByRole('option', { name: '2' })).toBeVisible(); - await expect(page.getByRole('option', { name: 'three' })).toBeVisible(); - await expect(page.getByRole('option', { name: '4' })).toBeVisible(); - await expect(page.getByRole('option', { name: 'undefined' })).toBeVisible(); +async function waitForComponents(page: Page, frame: Page | FrameLocator = page, isEditor = false) { + await frame.locator('text="foo button"').waitFor({ state: 'visible' }); + await frame.locator('img[alt="foo image"]').waitFor({ state: 'attached' }); + await frame.locator('text="foo datagrid column"').waitFor({ state: 'visible' }); + await frame.locator('text="custom component 1"').waitFor({ state: 'visible' }); + await frame.locator('label:has-text("foo textfield")').waitFor({ state: 'visible' }); + await frame.locator('text="foo typography"').waitFor({ state: 'visible' }); + await frame.locator('label:has-text("foo select")').waitFor({ state: 'visible' }); + + const optionsSelect = frame.getByRole('button', { name: /select with options/ }); + await optionsSelect.scrollIntoViewIfNeeded(); + if (isEditor) { + await clickCenter(page, optionsSelect); + } + await clickCenter(page, optionsSelect); + await expect(frame.getByRole('option', { name: 'one' })).toBeVisible(); + await expect(frame.getByRole('option', { name: '2' })).toBeVisible(); + await expect(frame.getByRole('option', { name: 'three' })).toBeVisible(); + await expect(frame.getByRole('option', { name: '4' })).toBeVisible(); + await expect(frame.getByRole('option', { name: 'undefined' })).toBeVisible(); } test('components runtime', async ({ page, api }) => { @@ -46,5 +51,5 @@ test('components editor', async ({ page, browserName, api }) => { const editorModel = new ToolpadEditor(page, browserName); editorModel.goto(app.id); - await waitForComponents(editorModel.appCanvas); + await waitForComponents(page, editorModel.appCanvas, true); }); From 6bd8f686d3f92afb20ff948b184b5307ea6ded96 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 1 Feb 2023 11:24:22 +0100 Subject: [PATCH 3/4] fixz --- .../integration/components/componentsDom.json | 32 +++++++--- test/integration/components/index.spec.ts | 63 ++++++++++++------- 2 files changed, 65 insertions(+), 30 deletions(-) diff --git a/test/integration/components/componentsDom.json b/test/integration/components/componentsDom.json index 83bb60d042b..b2378f080dc 100644 --- a/test/integration/components/componentsDom.json +++ b/test/integration/components/componentsDom.json @@ -313,8 +313,22 @@ "parentProp": "children", "parentIndex": "a0" }, - "5c43usr": { - "name": "pageRow1", + "ly03uc8": { + "name": "select", + "attributes": { + "title": { + "type": "const", + "value": "select" + } + }, + "id": "ly03uc8", + "type": "page", + "parentId": "vkowdut", + "parentProp": "pages", + "parentIndex": "a2" + }, + "vy33um7": { + "name": "pageRow", "props": {}, "attributes": { "component": { @@ -323,14 +337,14 @@ } }, "layout": {}, - "id": "5c43usr", + "id": "vy33um7", "type": "element", - "parentId": "f703ps3", + "parentId": "ly03uc8", "parentProp": "children", - "parentIndex": "a6" + "parentIndex": "a0" }, - "6n23u2h": { - "name": "selectWithOptions", + "q813u5k": { + "name": "selectOptions", "props": { "options": { "type": "jsExpression", @@ -352,9 +366,9 @@ } }, "layout": {}, - "id": "6n23u2h", + "id": "q813u5k", "type": "element", - "parentId": "5c43usr", + "parentId": "vy33um7", "parentProp": "children", "parentIndex": "a0" } diff --git a/test/integration/components/index.spec.ts b/test/integration/components/index.spec.ts index 23e19249e6a..6f97d8ed744 100644 --- a/test/integration/components/index.spec.ts +++ b/test/integration/components/index.spec.ts @@ -7,28 +7,29 @@ import { readJsonFile } from '../../utils/fs'; import generateId from '../../utils/generateId'; async function waitForComponents(page: Page, frame: Page | FrameLocator = page, isEditor = false) { - await frame.locator('text="foo button"').waitFor({ state: 'visible' }); - await frame.locator('img[alt="foo image"]').waitFor({ state: 'attached' }); - await frame.locator('text="foo datagrid column"').waitFor({ state: 'visible' }); - await frame.locator('text="custom component 1"').waitFor({ state: 'visible' }); - await frame.locator('label:has-text("foo textfield")').waitFor({ state: 'visible' }); - await frame.locator('text="foo typography"').waitFor({ state: 'visible' }); - await frame.locator('label:has-text("foo select")').waitFor({ state: 'visible' }); - - const optionsSelect = frame.getByRole('button', { name: /select with options/ }); - await optionsSelect.scrollIntoViewIfNeeded(); - if (isEditor) { - await clickCenter(page, optionsSelect); - } - await clickCenter(page, optionsSelect); - await expect(frame.getByRole('option', { name: 'one' })).toBeVisible(); - await expect(frame.getByRole('option', { name: '2' })).toBeVisible(); - await expect(frame.getByRole('option', { name: 'three' })).toBeVisible(); - await expect(frame.getByRole('option', { name: '4' })).toBeVisible(); - await expect(frame.getByRole('option', { name: 'undefined' })).toBeVisible(); + const button = frame.locator('text="foo button"'); + await button.waitFor({ state: 'visible' }); + + const image = frame.locator('img[alt="foo image"]'); + await image.waitFor({ state: 'attached' }); + + const datagrid = frame.locator('text="foo datagrid column"'); + await datagrid.waitFor({ state: 'visible' }); + + const customComponent = frame.locator('text="custom component 1"'); + await customComponent.waitFor({ state: 'visible' }); + + const textField = frame.locator('label:has-text("foo textfield")'); + await textField.waitFor({ state: 'visible' }); + + const text = frame.locator('text="foo typography"'); + await text.waitFor({ state: 'visible' }); + + const select = frame.locator('label:has-text("foo select")'); + await select.waitFor({ state: 'visible' }); } -test('components runtime', async ({ page, api }) => { +test('rendering components in the app runtime', async ({ page, api }) => { const dom = await readJsonFile(path.resolve(__dirname, './componentsDom.json')); const app = await api.mutation.createApp(`App ${generateId()}`, { @@ -41,7 +42,7 @@ test('components runtime', async ({ page, api }) => { await waitForComponents(page); }); -test('components editor', async ({ page, browserName, api }) => { +test('rendering components in the app editor', async ({ page, browserName, api }) => { const dom = await readJsonFile(path.resolve(__dirname, './componentsDom.json')); const app = await api.mutation.createApp(`App ${generateId()}`, { @@ -53,3 +54,23 @@ test('components editor', async ({ page, browserName, api }) => { await waitForComponents(page, editorModel.appCanvas, true); }); + +test('select component behavior', async ({ page, api }) => { + const dom = await readJsonFile(path.resolve(__dirname, './componentsDom.json')); + + const app = await api.mutation.createApp(`App ${generateId()}`, { + from: { kind: 'dom', dom }, + }); + + const runtimeModel = new ToolpadRuntime(page); + await runtimeModel.gotoPage(app.id, 'select'); + + const optionsSelect = page.getByRole('button', { name: /select with options/ }); + await optionsSelect.scrollIntoViewIfNeeded(); + await clickCenter(page, optionsSelect); + await expect(page.getByRole('option', { name: 'one' })).toBeVisible(); + await expect(page.getByRole('option', { name: '2' })).toBeVisible(); + await expect(page.getByRole('option', { name: 'three' })).toBeVisible(); + await expect(page.getByRole('option', { name: '4' })).toBeVisible(); + await expect(page.getByRole('option', { name: 'undefined' })).toBeVisible(); +}); From 258024f03528f3d9c0ccddab3c36d050ccfdab7b Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 1 Feb 2023 11:48:54 +0100 Subject: [PATCH 4/4] remove unnecessary --- test/integration/components/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/components/index.spec.ts b/test/integration/components/index.spec.ts index 6f97d8ed744..bdb75e47437 100644 --- a/test/integration/components/index.spec.ts +++ b/test/integration/components/index.spec.ts @@ -6,7 +6,7 @@ import clickCenter from '../../utils/clickCenter'; import { readJsonFile } from '../../utils/fs'; import generateId from '../../utils/generateId'; -async function waitForComponents(page: Page, frame: Page | FrameLocator = page, isEditor = false) { +async function waitForComponents(page: Page, frame: Page | FrameLocator = page) { const button = frame.locator('text="foo button"'); await button.waitFor({ state: 'visible' });