diff --git a/dist/tiny-form-fields.esm.js b/dist/tiny-form-fields.esm.js index c1d0720..f8919ea 100644 --- a/dist/tiny-form-fields.esm.js +++ b/dist/tiny-form-fields.esm.js @@ -11538,10 +11538,10 @@ var $author$project$Main$visibilityRuleSection = F4( var datalistElement = function () { if (!selectedField.$) { var field = selectedField.a; - var _v7 = field.a; - switch (_v7.$) { + var _v8 = field.a; + switch (_v8.$) { case 2: - var choices = _v7.a.k; + var choices = _v8.a.k; return $elm$core$Maybe$Just( A2( $elm$html$Html$datalist, @@ -11562,7 +11562,7 @@ var $author$project$Main$visibilityRuleSection = F4( }, choices))); case 3: - var choices = _v7.a.k; + var choices = _v8.a.k; return $elm$core$Maybe$Just( A2( $elm$html$Html$datalist, @@ -11583,7 +11583,7 @@ var $author$project$Main$visibilityRuleSection = F4( }, choices))); case 4: - var choices = _v7.a.k; + var choices = _v8.a.k; return $elm$core$Maybe$Just( A2( $elm$html$Html$datalist, @@ -11723,25 +11723,39 @@ var $author$project$Main$visibilityRuleSection = F4( } }(); var textInputNode = A2( - $elm$html$Html$input, - _Utils_ap( - _List_fromArray( - [ - $elm$html$Html$Attributes$type_('text'), - $elm$html$Html$Attributes$value(comparisonValueString), - $elm$html$Html$Events$onInput( - function (str) { - return A3( - $author$project$Main$OnFormField, - A3($author$project$Main$OnVisibilityConditionValueInput, ruleIndex, conditionIndex, str), - fieldIndex, - ''); - }), - $elm$html$Html$Attributes$required(true), - $elm$html$Html$Attributes$class('tff-comparison-value') - ]), - datalistAttr), - _List_Nil); + $elm$html$Html$div, + _List_Nil, + A2( + $elm$core$List$cons, + A2( + $elm$html$Html$input, + _Utils_ap( + _List_fromArray( + [ + $elm$html$Html$Attributes$type_('text'), + $elm$html$Html$Attributes$value(comparisonValueString), + $elm$html$Html$Events$onInput( + function (str) { + return A3( + $author$project$Main$OnFormField, + A3($author$project$Main$OnVisibilityConditionValueInput, ruleIndex, conditionIndex, str), + fieldIndex, + ''); + }), + $elm$html$Html$Attributes$required(true), + $elm$html$Html$Attributes$class('tff-comparison-value') + ]), + datalistAttr), + _List_Nil), + function () { + if (!datalistElement.$) { + var element = datalistElement.a; + return _List_fromArray( + [element]); + } else { + return _List_Nil; + } + }())); var candidateFields = A2($author$project$Main$otherQuestionTitles, formFields, fieldIndex); var candidateFieldsExceptSelf = A2( $elm$core$List$filter, diff --git a/dist/tiny-form-fields.js b/dist/tiny-form-fields.js index 1d49ef0..77819db 100644 --- a/dist/tiny-form-fields.js +++ b/dist/tiny-form-fields.js @@ -11530,10 +11530,10 @@ var $author$project$Main$visibilityRuleSection = F4( var datalistElement = function () { if (!selectedField.$) { var field = selectedField.a; - var _v7 = field.a; - switch (_v7.$) { + var _v8 = field.a; + switch (_v8.$) { case 2: - var choices = _v7.a.k; + var choices = _v8.a.k; return $elm$core$Maybe$Just( A2( $elm$html$Html$datalist, @@ -11554,7 +11554,7 @@ var $author$project$Main$visibilityRuleSection = F4( }, choices))); case 3: - var choices = _v7.a.k; + var choices = _v8.a.k; return $elm$core$Maybe$Just( A2( $elm$html$Html$datalist, @@ -11575,7 +11575,7 @@ var $author$project$Main$visibilityRuleSection = F4( }, choices))); case 4: - var choices = _v7.a.k; + var choices = _v8.a.k; return $elm$core$Maybe$Just( A2( $elm$html$Html$datalist, @@ -11715,25 +11715,39 @@ var $author$project$Main$visibilityRuleSection = F4( } }(); var textInputNode = A2( - $elm$html$Html$input, - _Utils_ap( - _List_fromArray( - [ - $elm$html$Html$Attributes$type_('text'), - $elm$html$Html$Attributes$value(comparisonValueString), - $elm$html$Html$Events$onInput( - function (str) { - return A3( - $author$project$Main$OnFormField, - A3($author$project$Main$OnVisibilityConditionValueInput, ruleIndex, conditionIndex, str), - fieldIndex, - ''); - }), - $elm$html$Html$Attributes$required(true), - $elm$html$Html$Attributes$class('tff-comparison-value') - ]), - datalistAttr), - _List_Nil); + $elm$html$Html$div, + _List_Nil, + A2( + $elm$core$List$cons, + A2( + $elm$html$Html$input, + _Utils_ap( + _List_fromArray( + [ + $elm$html$Html$Attributes$type_('text'), + $elm$html$Html$Attributes$value(comparisonValueString), + $elm$html$Html$Events$onInput( + function (str) { + return A3( + $author$project$Main$OnFormField, + A3($author$project$Main$OnVisibilityConditionValueInput, ruleIndex, conditionIndex, str), + fieldIndex, + ''); + }), + $elm$html$Html$Attributes$required(true), + $elm$html$Html$Attributes$class('tff-comparison-value') + ]), + datalistAttr), + _List_Nil), + function () { + if (!datalistElement.$) { + var element = datalistElement.a; + return _List_fromArray( + [element]); + } else { + return _List_Nil; + } + }())); var candidateFields = A2($author$project$Main$otherQuestionTitles, formFields, fieldIndex); var candidateFieldsExceptSelf = A2( $elm$core$List$filter, diff --git a/e2e/datalist-visibility-rules.spec.ts b/e2e/datalist-visibility-rules.spec.ts new file mode 100644 index 0000000..607dd91 --- /dev/null +++ b/e2e/datalist-visibility-rules.spec.ts @@ -0,0 +1,307 @@ +import { test, expect } from '@playwright/test'; + +test.describe.configure({ mode: 'parallel' }); + +test.use({ browserName: 'firefox' }); + +test.describe('Datalist for visibility rule values', () => { + test('should show datalist for Radio buttons', async ({ page }) => { + // Navigate to the form builder + await page.goto( + 'http://localhost:8000/#viewMode=Editor&formFields=%5B%5D&_url=http://localhost:9000/post' + ); + + // Add a Radio buttons field + await page.getByRole('button', { name: 'Radio buttons' }).click(); + + // Click on the field's icon to select it for editing + await page.locator('svg').first().click(); + + // Now update the choices + await page.getByRole('textbox', { name: 'Choices' }).fill('Red\nBlue\nGreen'); + + // Wait for Elm to process and persist the choices update (it updates the URL hash) + await page.waitForFunction( + () => { + return ( + window.location.hash.includes('Red') && + window.location.hash.includes('Blue') && + window.location.hash.includes('Green') + ); + }, + { timeout: 2000 } + ); + + // Add a Single-line free text field + await page.getByRole('button', { name: 'Single-line free text' }).click(); + + // Click on the second field's icon to select it + const secondFieldIcon = page.locator('svg').nth(1); + await secondFieldIcon.click(); + + // Wait for field settings to render + await page.waitForTimeout(100); + + // Add field logic + await page.getByRole('button', { name: 'Add field logic' }).click(); + + // Wait for the field logic UI to render and the field selector dropdown to populate + await page.waitForTimeout(500); + + // Wait for the field condition select to have the "value of" options populated + await page.waitForFunction( + () => { + const selects = document.querySelectorAll('select'); + for (let i = 0; i < selects.length; i++) { + const options = Array.from(selects[i].options).map((opt) => opt.text); + if (options.some((text) => text.includes('value of'))) { + return true; + } + } + return false; + }, + { timeout: 5000 } + ); + + // Find the select that has "value of" options and select the Radio buttons field + const selectIndex = await page.evaluate(() => { + const selects = document.querySelectorAll('select'); + for (let i = 0; i < selects.length; i++) { + const options = Array.from(selects[i].options).map((opt) => opt.text); + if (options.some((text) => text.includes('value of'))) { + return i; + } + } + return -1; + }); + + await page + .locator('select') + .nth(selectIndex) + .selectOption({ label: 'value of "Radio buttons question 1"' }); + + // At this point, the visibility rule UI should be showing with a text input + // that has a datalist for the comparison value + + // Wait for the datalist element to appear in the DOM (it will be hidden, which is normal for datalists) + await page.waitForSelector('datalist', { state: 'attached', timeout: 5000 }); + + // Verify datalist exists in the DOM + const datalists = await page.evaluate(() => { + const dls = document.querySelectorAll('datalist'); + console.log('Found datalists:', dls.length); + const textInputs = document.querySelectorAll('input[type="text"]'); + console.log('Found text inputs:', textInputs.length); + textInputs.forEach((inp, i) => { + console.log(`Input ${i}:`, { + value: inp.value, + list: inp.getAttribute('list'), + }); + }); + return { + totalDatalists: dls.length, + datalistDetails: Array.from(dls).map((dl) => ({ + id: dl.id, + options: Array.from(dl.options).map((opt) => opt.value), + })), + }; + }); + + // Should have exactly 1 datalist + expect(datalists.totalDatalists).toBe(1); + + // Should have the correct options + expect(datalists.datalistDetails[0].options).toEqual(['Red', 'Blue', 'Green']); + + // Verify the datalist ID matches the expected pattern + expect(datalists.datalistDetails[0].id).toMatch(/^datalist-1-0-0$/); + + // Verify the input element has the correct list attribute + const inputs = await page.evaluate(() => { + const textInputs = document.querySelectorAll('input[type="text"]'); + return Array.from(textInputs).map((inp, i) => ({ + index: i, + value: inp.value, + list: inp.getAttribute('list'), + hasDatalist: inp.list !== null, + })); + }); + + // Find the input with the datalist + const inputWithDatalist = inputs.find((inp) => inp.list === 'datalist-1-0-0'); + expect(inputWithDatalist).toBeTruthy(); + expect(inputWithDatalist?.hasDatalist).toBe(true); + }); + + // NOTE: This test is skipped due to DOM layout issue where the Dropdown's