Grow the Add stories field to fit its URLs

This commit is contained in:
tim
2026-09-27 23:38:28 +02:00
parent ce19035689
commit a7a89b145c
3 changed files with 16 additions and 1 deletions
+8
View File
@@ -227,6 +227,13 @@ export function storiesPage(main, _params, config) {
rows: 3, rows: 3,
placeholder: 'https://www.royalroad.com/fiction/… (one URL per line)', placeholder: 'https://www.royalroad.com/fiction/… (one URL per line)',
'aria-label': 'Story URLs', 'aria-label': 'Story URLs',
// Phones give no handle to resize a textarea, so the field grows to fit its URLs. It never
// shrinks, so a size set with the handle on a desktop stays.
oninput: () => {
if (urls.scrollHeight > urls.clientHeight) {
urls.style.height = `${urls.scrollHeight + urls.offsetHeight - urls.clientHeight}px`;
}
},
}); });
const add = button( const add = button(
'Add', 'Add',
@@ -237,6 +244,7 @@ export function storiesPage(main, _params, config) {
if (result.jobs.length) { if (result.jobs.length) {
toast(`Queued ${result.jobs.length} ${result.jobs.length === 1 ? 'story' : 'stories'}`); toast(`Queued ${result.jobs.length} ${result.jobs.length === 1 ? 'story' : 'stories'}`);
urls.value = ''; urls.value = '';
urls.style.height = '';
refreshJobs(); refreshJobs();
} }
}, },
+1
View File
@@ -12,6 +12,7 @@ export class Element {
this.checked = false; this.checked = false;
this.disabled = false; this.disabled = false;
this.files = []; this.files = [];
this.style = {};
} }
append(...children) { append(...children) {
for (const child of children) { for (const child of children) {
+7 -1
View File
@@ -88,7 +88,12 @@ test('failed settings saves retain edits and re-enable the action', async () =>
test('adding stories keeps input on rejection and clears it after a mixed result', async () => { test('adding stories keeps input on rejection and clears it after a mixed result', async () => {
await mountStories({ ...config, library_available: false }); await mountStories({ ...config, library_available: false });
const urls = nodes(main).find((el) => el.tagName === 'textarea'); const urls = nodes(main).find((el) => el.tagName === 'textarea');
urls.value = ' https://site.test/1\n\nhttps://site.test/2 '; Object.assign(urls, { scrollHeight: 90, clientHeight: 60, offsetHeight: 62 });
input(urls, ' https://site.test/1\n\nhttps://site.test/2 ');
assert.equal(urls.style.height, '92px');
Object.assign(urls, { scrollHeight: 50, clientHeight: 90, offsetHeight: 92 });
input(urls, urls.value);
assert.equal(urls.style.height, '92px');
api.on('POST', 'stories', { error: 'Unavailable' }, 503); api.on('POST', 'stories', { error: 'Unavailable' }, 503);
await click(main, 'Add'); await click(main, 'Add');
assert.match(urls.value, /site.test/); assert.match(urls.value, /site.test/);
@@ -96,6 +101,7 @@ test('adding stories keeps input on rejection and clears it after a mixed result
await click(main, 'Add'); await click(main, 'Add');
assert.deepEqual(api.sent('POST', 'stories').at(-1).body, { urls: ' https://site.test/1\n\nhttps://site.test/2 ' }); assert.deepEqual(api.sent('POST', 'stories').at(-1).body, { urls: ' https://site.test/1\n\nhttps://site.test/2 ' });
assert.equal(urls.value, ''); assert.equal(urls.value, '');
assert.equal(urls.style.height, '');
assert.match(ui.toasts.textContent, /Unsupported URL/); assert.match(ui.toasts.textContent, /Unsupported URL/);
await click(main, 'Add'); await click(main, 'Add');
assert.equal(api.sent('POST', 'stories').length, 2); assert.equal(api.sent('POST', 'stories').length, 2);