Skip to content

Commit 6752cd8

Browse files
committed
Fix datalist visibility - ensure datalist element appears in DOM
Datalist elements were missing from DOM when visibility rules referenced choice fields. This fix ensures the datalist is properly rendered with the choice field's options. Fixes the bug demonstrated in previous commit's tests.
1 parent 6d405c0 commit 6752cd8

File tree

4 files changed

+154
-83
lines changed

4 files changed

+154
-83
lines changed

dist/tiny-form-fields.esm.js

Lines changed: 38 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/tiny-form-fields.js

Lines changed: 38 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

e2e/datalist-visibility-rules.spec.ts

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,16 @@ test.describe('Datalist for visibility rule values', () => {
2121
await page.getByRole('textbox', { name: 'Choices' }).fill('Red\nBlue\nGreen');
2222

2323
// Wait for Elm to process and persist the choices update (it updates the URL hash)
24-
await page.waitForFunction(() => {
25-
return window.location.hash.includes('Red') && window.location.hash.includes('Blue') && window.location.hash.includes('Green');
26-
}, { timeout: 2000 });
24+
await page.waitForFunction(
25+
() => {
26+
return (
27+
window.location.hash.includes('Red') &&
28+
window.location.hash.includes('Blue') &&
29+
window.location.hash.includes('Green')
30+
);
31+
},
32+
{ timeout: 2000 }
33+
);
2734

2835
// Add a Single-line free text field
2936
await page.getByRole('button', { name: 'Single-line free text' }).click();
@@ -42,30 +49,36 @@ test.describe('Datalist for visibility rule values', () => {
4249
await page.waitForTimeout(500);
4350

4451
// Wait for the field condition select to have the "value of" options populated
45-
await page.waitForFunction(() => {
46-
const selects = document.querySelectorAll('select');
47-
for (let i = 0; i < selects.length; i++) {
48-
const options = Array.from(selects[i].options).map(opt => opt.text);
49-
if (options.some(text => text.includes('value of'))) {
50-
return true;
52+
await page.waitForFunction(
53+
() => {
54+
const selects = document.querySelectorAll('select');
55+
for (let i = 0; i < selects.length; i++) {
56+
const options = Array.from(selects[i].options).map((opt) => opt.text);
57+
if (options.some((text) => text.includes('value of'))) {
58+
return true;
59+
}
5160
}
52-
}
53-
return false;
54-
}, { timeout: 5000 });
61+
return false;
62+
},
63+
{ timeout: 5000 }
64+
);
5565

5666
// Find the select that has "value of" options and select the Radio buttons field
5767
const selectIndex = await page.evaluate(() => {
5868
const selects = document.querySelectorAll('select');
5969
for (let i = 0; i < selects.length; i++) {
60-
const options = Array.from(selects[i].options).map(opt => opt.text);
61-
if (options.some(text => text.includes('value of'))) {
70+
const options = Array.from(selects[i].options).map((opt) => opt.text);
71+
if (options.some((text) => text.includes('value of'))) {
6272
return i;
6373
}
6474
}
6575
return -1;
6676
});
6777

68-
await page.locator('select').nth(selectIndex).selectOption({ label: 'value of "Radio buttons question 1"' });
78+
await page
79+
.locator('select')
80+
.nth(selectIndex)
81+
.selectOption({ label: 'value of "Radio buttons question 1"' });
6982

7083
// At this point, the visibility rule UI should be showing with a text input
7184
// that has a datalist for the comparison value
@@ -82,7 +95,7 @@ test.describe('Datalist for visibility rule values', () => {
8295
textInputs.forEach((inp, i) => {
8396
console.log(`Input ${i}:`, {
8497
value: inp.value,
85-
list: inp.getAttribute('list')
98+
list: inp.getAttribute('list'),
8699
});
87100
});
88101
return {
@@ -145,10 +158,17 @@ test.describe('Datalist for visibility rule values', () => {
145158
await page.getByRole('textbox', { name: 'Choices' }).fill('Option A\nOption B\nOption C');
146159

147160
// Wait for Elm to process and persist the choices update
148-
await page.waitForFunction(() => {
149-
const hash = decodeURIComponent(window.location.hash);
150-
return hash.includes('Option A') && hash.includes('Option B') && hash.includes('Option C');
151-
}, { timeout: 2000 });
161+
await page.waitForFunction(
162+
() => {
163+
const hash = decodeURIComponent(window.location.hash);
164+
return (
165+
hash.includes('Option A') &&
166+
hash.includes('Option B') &&
167+
hash.includes('Option C')
168+
);
169+
},
170+
{ timeout: 2000 }
171+
);
152172

153173
// Add a Single-line free text field
154174
await page.getByRole('button', { name: 'Single-line free text' }).click();
@@ -167,7 +187,10 @@ test.describe('Datalist for visibility rule values', () => {
167187
await page.waitForTimeout(500);
168188

169189
// Select the Dropdown field in the visibility condition dropdown explicitly
170-
await page.locator('select').nth(1).selectOption({ label: 'value of "Dropdown question 1"' });
190+
await page
191+
.locator('select')
192+
.nth(1)
193+
.selectOption({ label: 'value of "Dropdown question 1"' });
171194

172195
// Wait for the datalist element to appear in the DOM (it will be hidden, which is normal for datalists)
173196
await page.waitForSelector('datalist', { state: 'attached', timeout: 5000 });
@@ -202,10 +225,18 @@ test.describe('Datalist for visibility rule values', () => {
202225
.fill('Choice 1\nChoice 2\nChoice 3\nChoice 4');
203226

204227
// Wait for Elm to process and persist the choices update
205-
await page.waitForFunction(() => {
206-
const hash = decodeURIComponent(window.location.hash);
207-
return hash.includes('Choice 1') && hash.includes('Choice 2') && hash.includes('Choice 3') && hash.includes('Choice 4');
208-
}, { timeout: 2000 });
228+
await page.waitForFunction(
229+
() => {
230+
const hash = decodeURIComponent(window.location.hash);
231+
return (
232+
hash.includes('Choice 1') &&
233+
hash.includes('Choice 2') &&
234+
hash.includes('Choice 3') &&
235+
hash.includes('Choice 4')
236+
);
237+
},
238+
{ timeout: 2000 }
239+
);
209240

210241
// Add a Single-line free text field
211242
await page.getByRole('button', { name: 'Single-line free text' }).click();
@@ -224,7 +255,10 @@ test.describe('Datalist for visibility rule values', () => {
224255
await page.waitForTimeout(500);
225256

226257
// Select the Checkboxes field in the visibility condition dropdown explicitly
227-
await page.locator('select').nth(1).selectOption({ label: 'value of "Checkboxes question 1"' });
258+
await page
259+
.locator('select')
260+
.nth(1)
261+
.selectOption({ label: 'value of "Checkboxes question 1"' });
228262

229263
// Wait for the datalist element to appear in the DOM (it will be hidden, which is normal for datalists)
230264
await page.waitForSelector('datalist', { state: 'attached', timeout: 5000 });

src/Main.elm

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2808,16 +2808,25 @@ visibilityRuleSection fieldIndex formFields ruleIndex visibilityRule =
28082808
v
28092809

28102810
textInputNode =
2811-
input
2812-
([ type_ "text"
2813-
, value comparisonValueString
2814-
, onInput (\str -> OnFormField (OnVisibilityConditionValueInput ruleIndex conditionIndex str) fieldIndex "")
2815-
, required True
2816-
, class "tff-comparison-value"
2817-
]
2818-
++ datalistAttr
2811+
div []
2812+
(input
2813+
([ type_ "text"
2814+
, value comparisonValueString
2815+
, onInput (\str -> OnFormField (OnVisibilityConditionValueInput ruleIndex conditionIndex str) fieldIndex "")
2816+
, required True
2817+
, class "tff-comparison-value"
2818+
]
2819+
++ datalistAttr
2820+
)
2821+
[]
2822+
:: (case datalistElement of
2823+
Just element ->
2824+
[ element ]
2825+
2826+
Nothing ->
2827+
[]
2828+
)
28192829
)
2820-
[]
28212830

28222831
fieldSelectNode =
28232832
div

0 commit comments

Comments
 (0)