Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,6 @@ <h1 class="sec-title" id="ev-title">General</h1>
</div></div>
</div>

<script type="module" src="/js/admin.js?v=2aaed21c"></script>
<script type="module" src="/js/admin.js?v=10b7bd5e"></script>
</body>
</html>
8 changes: 4 additions & 4 deletions ui/js/admin-app-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
failureIsMissingApiPath,
nextActiveIndex,
sameIconName,
} from '/js/admin-logic.js?v=fc7f0836';
} from '/js/admin-logic.js?v=1cca36f5';
import { t } from '/js/i18n.js?v=ed7c5565';
import {
toast,
Expand All @@ -31,10 +31,10 @@ import {
reveal,
setTogDisabled,
swapContent,
} from '/js/admin-shared.js?v=da7df247';
import { createListbox } from '/js/listbox.js?v=4afb5969';
} from '/js/admin-shared.js?v=5e6a023a';
import { createListbox } from '/js/listbox.js?v=7d7867ae';
import { MAX_LABELS } from '/js/badge-logic.js?v=5c72bd4c';
import { renderColorControl, BADGE_DEFAULT } from '/js/admin-color-control.js?v=82916010';
import { renderColorControl, BADGE_DEFAULT } from '/js/admin-color-control.js?v=64eef54d';
import { badgeErrorAdvice, TONE } from '/js/admin-error.js?v=b0881957';
import { fluidHoverClear, fluidHoverKb } from '/js/fluid-hover.js?v=cb886e86';
import { iconSvg } from '/js/icon-set.js?v=628341d4';
Expand Down
2 changes: 1 addition & 1 deletion ui/js/admin-auth.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { apiGet, apiPost } from '/js/admin-shared.js?v=da7df247';
import { apiGet, apiPost } from '/js/admin-shared.js?v=5e6a023a';
import { initI18n, t } from '/js/i18n.js?v=ed7c5565';
import { loginErrorKey } from '/js/admin-error.js?v=b0881957';
import { pwStrength } from '/js/password-strength.js?v=389e0ed0';
Expand Down
2 changes: 1 addition & 1 deletion ui/js/admin-color-control.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PE_SVG, initInlineEdit, toast, reveal } from '/js/admin-shared.js?v=da7df247';
import { PE_SVG, initInlineEdit, toast, reveal } from '/js/admin-shared.js?v=5e6a023a';
import { t } from '/js/i18n.js?v=ed7c5565';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { qa, q } from '/js/utils.js?v=dc4ac52e';
Expand Down
6 changes: 3 additions & 3 deletions ui/js/admin-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@

import { collapsedFolders, filter, state } from '/js/admin-state.js?v=af772a1b';
import { snapshotItems } from '/js/admin-save-logic.js?v=5613a9fa';
import { reorderItems } from '/js/admin-logic.js?v=fc7f0836';
import { reorderItems } from '/js/admin-logic.js?v=1cca36f5';
import { initDrag, wireRowDrag } from '/js/admin-drag.js?v=29e09aad';
import { paintIcon } from '/js/admin-shared.js?v=da7df247';
import { paintIcon } from '/js/admin-shared.js?v=5e6a023a';
import { clr, el, focusFirst, initial, qa, setUserText } from '/js/utils.js?v=dc4ac52e';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { t } from '/js/i18n.js?v=ed7c5565';
import { sizeLabel } from '/js/admin-widget-form.js?v=047fef40';
import { sizeLabel } from '/js/admin-widget-form.js?v=e0966417';
import { widgetGlyph } from '/js/widget-glyphs.js?v=b383b7c8';
import { iconSvg } from '/js/icon-set.js?v=628341d4';

Expand Down
10 changes: 10 additions & 0 deletions ui/js/admin-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,13 @@ export function createDirtyTracker(read) {
},
};
}

/** What a single-choice picker shows as chosen. An empty string is a choice
only when an option carries it.
@param {unknown} value @param {{ value?: unknown, group?: unknown }[]} options
@returns {string[]} */
export function singleChoice(value, options) {
if (value == null) return [];
const v = String(value);
return v !== '' || options.some(o => o.group == null && String(o.value) === '') ? [v] : [];
}
6 changes: 3 additions & 3 deletions ui/js/admin-settings.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { toast, apiGet, apiPost, errorText, reveal, swapContent, wireInlineEdit } from '/js/admin-shared.js?v=da7df247';
import { toast, apiGet, apiPost, errorText, reveal, swapContent, wireInlineEdit } from '/js/admin-shared.js?v=5e6a023a';
import { socketProbeAdvice } from '/js/admin-error.js?v=b0881957';
import { pwStrength } from '/js/password-strength.js?v=389e0ed0';
import { t } from '/js/i18n.js?v=ed7c5565';
Expand All @@ -9,12 +9,12 @@ import {
needsCurrentPassword,
createDirtyTracker,
BLOCK,
} from '/js/admin-logic.js?v=fc7f0836';
} from '/js/admin-logic.js?v=1cca36f5';
import { confirmText, promptModal } from '/js/modal.js?v=6b0320bd';
import { el, inp, setUserText } from '/js/utils.js?v=dc4ac52e';
import { formatNumber } from '/js/format-number.js?v=abcd4ae4';
import { serialWrites } from '/js/admin-save-logic.js?v=5613a9fa';
import { renderColorControl } from '/js/admin-color-control.js?v=82916010';
import { renderColorControl } from '/js/admin-color-control.js?v=64eef54d';
import { BACKDROP } from '/js/background.js?v=d6f2e701';
import {
ALLOWED_HOSTS_MAX,
Expand Down
2 changes: 1 addition & 1 deletion ui/js/admin-shared.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Stateless helpers shared by the admin modules. Mutable state stays out. */
import { recoversSession, toastHoldMs } from '/js/admin-logic.js?v=fc7f0836';
import { recoversSession, toastHoldMs } from '/js/admin-logic.js?v=1cca36f5';
import { el, isolate, q } from '/js/utils.js?v=dc4ac52e';
import { t } from '/js/i18n.js?v=ed7c5565';
import { errorAdvice } from '/js/admin-error.js?v=b0881957';
Expand Down
8 changes: 4 additions & 4 deletions ui/js/admin-widget-form.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { state } from '/js/admin-state.js?v=af772a1b';
import { PE_SVG, initInlineEdit, swapContent } from '/js/admin-shared.js?v=da7df247';
import { createListbox } from '/js/listbox.js?v=4afb5969';
import { renderWidgetConfigForm } from '/js/widget-config-form.js?v=c4755d57';
import { PE_SVG, initInlineEdit, swapContent } from '/js/admin-shared.js?v=5e6a023a';
import { createListbox } from '/js/listbox.js?v=7d7867ae';
import { renderWidgetConfigForm } from '/js/widget-config-form.js?v=294805cc';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { sizesForView, widgetConfigMode, rejectionLines, carriesTypedValues } from '/js/admin-logic.js?v=fc7f0836';
import { sizesForView, widgetConfigMode, rejectionLines, carriesTypedValues } from '/js/admin-logic.js?v=1cca36f5';
import { t } from '/js/i18n.js?v=ed7c5565';
import { focusFirst, q, qi, qa } from '/js/utils.js?v=dc4ac52e';
import { iconSvg } from '/js/icon-set.js?v=628341d4';
Expand Down
16 changes: 8 additions & 8 deletions ui/js/admin.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { buildAppForm, buildFolderForm, captureActLabels, serializeKvRows } from '/js/admin-app-form.js?v=92c6949c';
import { checkAuth, requireLogin, wirePasswordStrength } from '/js/admin-auth.js?v=73ed7df1';
import { buildAppForm, buildFolderForm, captureActLabels, serializeKvRows } from '/js/admin-app-form.js?v=deccbdee';
import { checkAuth, requireLogin, wirePasswordStrength } from '/js/admin-auth.js?v=104be7f0';
import { recoveryShown } from '/js/config-recovery.js?v=85138697';
import { focusRow, initList, render, syncFilterUI } from '/js/admin-list.js?v=02db1758';
import { resolveAdminSection } from '/js/admin-logic.js?v=fc7f0836';
import { focusRow, initList, render, syncFilterUI } from '/js/admin-list.js?v=14782161';
import { resolveAdminSection } from '/js/admin-logic.js?v=1cca36f5';
import {
afterImport,
buildAppItem,
Expand All @@ -23,7 +23,7 @@ import {
showBgFields,
showWallpaperFile,
wireUnsplashKey,
} from '/js/admin-settings.js?v=9ffe0860';
} from '/js/admin-settings.js?v=584aae78';
import {
apiGet,
apiPost,
Expand All @@ -37,13 +37,13 @@ import {
setReauthHandler,
toast,
withheldText,
} from '/js/admin-shared.js?v=da7df247';
} from '/js/admin-shared.js?v=5e6a023a';
import { collapsedFolders, filter, state } from '/js/admin-state.js?v=af772a1b';
import { buildWidgetForm } from '/js/admin-widget-form.js?v=047fef40';
import { buildWidgetForm } from '/js/admin-widget-form.js?v=e0966417';
import { initFluidHover } from '/js/fluid-hover.js?v=cb886e86';
import { formatNumber } from '/js/format-number.js?v=abcd4ae4';
import { initGlideSelect, syncGlideSelect } from '/js/glide-select.js?v=8b39e9d0';
import { createListbox } from '/js/listbox.js?v=4afb5969';
import { createListbox } from '/js/listbox.js?v=7d7867ae';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { initI18n, LANGUAGES, t } from '/js/i18n.js?v=ed7c5565';
import { loadLocalIcons } from '/js/icons.js?v=5ac01d72';
Expand Down
14 changes: 4 additions & 10 deletions ui/js/listbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Do not use popover. The top layer makes iOS collapse its browser toolbar,
which exposes a strip of page canvas and misplaces the menu. */

import { nextActiveIndex } from '/js/admin-logic.js?v=fc7f0836';
import { nextActiveIndex, singleChoice } from '/js/admin-logic.js?v=1cca36f5';
import { fluidHoverClear, fluidHoverKb } from '/js/fluid-hover.js?v=cb886e86';
import { formatList } from '/js/format-number.js?v=abcd4ae4';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
Expand Down Expand Up @@ -81,9 +81,7 @@ export function createListbox(
} = /** @type {any} */ ({}),
) {
let opts = options.slice();
let chosen = multiple
? new Set((Array.isArray(value) ? value : []).map(String))
: new Set(value == null || value === '' ? [] : [String(value)]);
let chosen = multiple ? new Set((Array.isArray(value) ? value : []).map(String)) : new Set(singleChoice(value, opts));

/* The list is not a descendant of its button, so the two are tied by id. */
const listId = id ? `${id}-list` : `lb-${++seq}`;
Expand Down Expand Up @@ -331,17 +329,13 @@ export function createListbox(
list,
getValue,
setValue(v) {
chosen = multiple
? new Set((Array.isArray(v) ? v : []).map(String))
: new Set(v == null || v === '' ? [] : [String(v)]);
chosen = multiple ? new Set((Array.isArray(v) ? v : []).map(String)) : new Set(singleChoice(v, opts));
paint();
},
setOptions(next, v) {
opts = (next || []).slice();
if (v !== undefined) {
chosen = multiple
? new Set((Array.isArray(v) ? v : []).map(String))
: new Set(v == null || v === '' ? [] : [String(v)]);
chosen = multiple ? new Set((Array.isArray(v) ? v : []).map(String)) : new Set(singleChoice(v, opts));
} else if (!multiple) {
/* A refreshed list may no longer carry what was chosen. */
const still = [...chosen].filter(c => opts.some(o => String(o.value) === c));
Expand Down
8 changes: 4 additions & 4 deletions ui/js/widget-config-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@

import { t } from '/js/i18n.js?v=ed7c5565';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { reveal, wireInlineEdit } from '/js/admin-shared.js?v=da7df247';
import { createListbox } from '/js/listbox.js?v=4afb5969';
import { renderColorControl } from '/js/admin-color-control.js?v=82916010';
import { reveal, wireInlineEdit } from '/js/admin-shared.js?v=5e6a023a';
import { createListbox } from '/js/listbox.js?v=7d7867ae';
import { renderColorControl } from '/js/admin-color-control.js?v=64eef54d';
import {
seedCarried,
applyOptionSet,
collectFieldValues,
requiredFieldMissing,
groupBounds,
visibleFieldFlags,
} from '/js/admin-logic.js?v=fc7f0836';
} from '/js/admin-logic.js?v=1cca36f5';
import { optionsErrorAdvice, TONE } from '/js/admin-error.js?v=b0881957';
import { qi } from '/js/utils.js?v=dc4ac52e';
import { formatNumber } from '/js/format-number.js?v=abcd4ae4';
Expand Down
18 changes: 18 additions & 0 deletions ui/test/admin-logic.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
failureIsMissingApiPath,
createDirtyTracker,
BLOCK,
singleChoice,
} from '../js/admin-logic.js';

test('createDirtyTracker is clean until the value moves, and clean again after reset', () => {
Expand Down Expand Up @@ -651,3 +652,20 @@ test('sameIconName treats nothing as nothing', () => {
assert.equal(sameIconName(null, undefined), false);
assert.equal(sameIconName('---', '***'), false);
});

test('an empty string is a choice when an option carries it', () => {
const opts = [
{ value: '', label: 'Default' },
{ value: 'no-referrer', label: 'no-referrer' },
];
assert.deepEqual(singleChoice('', opts), ['']);
assert.deepEqual(singleChoice('no-referrer', opts), ['no-referrer']);
});

test('an empty string or nothing is no choice otherwise', () => {
const opts = [{ value: 'a' }, { group: 'Heading', value: '' }];
assert.deepEqual(singleChoice('', opts), []);
assert.deepEqual(singleChoice(null, opts), []);
assert.deepEqual(singleChoice(undefined, opts), []);
assert.deepEqual(singleChoice(3, [{ value: 3 }]), ['3']);
});
5 changes: 5 additions & 0 deletions ui/test/listbox.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,8 @@ test('the language picker marks each language name with its language', () => {
'the button does not carry it',
);
});

test('every single-choice value goes through singleChoice', () => {
assert.equal((src.match(/new Set\(singleChoice\(/g) || []).length, 3);
assert.doesNotMatch(src, /=== '' \? \[\] :/);
});
Loading