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
Original file line number Diff line number Diff line change
Expand Up @@ -6732,7 +6732,11 @@ function bindFieldHelpTooltips() {
trigger.addEventListener("focus", () => showFieldHelpPopover(trigger));
trigger.addEventListener("blur", scheduleFieldHelpPopoverHide);
trigger.addEventListener("keydown", (event) => {
if (event.key === "Escape") hideFieldHelpPopover();
if (event.key === "Escape" && !popover.hidden) {
event.preventDefault();
event.stopPropagation();
hideFieldHelpPopover();
}
});
});
popover.addEventListener("mouseenter", clearFieldHelpHideTimer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -875,12 +875,15 @@ <h1>Blob Stores</h1>
<input id="blobstore-path" name="path" type="text" autocomplete="off">
</label>
<label class="s3-only">
<span>Credentials</span>
<select id="blobstore-credential-source" name="credentialSource" aria-describedby="blobstore-credential-help">
<span class="field-label">Credentials
<span id="blobstore-credential-help" class="field-help" tabindex="0" role="note" aria-label="Default credentials use EC2 instance profiles, EKS Pod Identity / IRSA, or other AWS credentials and clear saved keys. OSS Native requires static keys." data-tooltip="Default credentials use EC2 instance profiles, EKS Pod Identity / IRSA, or other AWS credentials and clear saved keys. OSS Native requires static keys.">
Comment thread
klboke marked this conversation as resolved.
<span class="lucide-icon icon-info" aria-hidden="true"></span>
</span>
</span>
<select id="blobstore-credential-source" name="credentialSource">
<option value="default">Default AWS credentials</option>
<option value="static">Static access key / secret key</option>
</select>
<small id="blobstore-credential-help">Default credentials use EC2 instance profiles, EKS Pod Identity / IRSA, or other AWS credentials and clear saved keys. OSS Native requires static keys.</small>
</label>
<label class="s3-only static-credentials">
<span>Access key <span class="required-mark">*</span></span>
Expand Down Expand Up @@ -1914,6 +1917,6 @@ <h1>Nexus Repository Data</h1>
<script src="./assets/admin-filter.js?v=20260706-wildcard-filter-1"></script>
<script src="/browse/assets/global-component-search.js?v=20260817-global-search-2"></script>
<script src="./assets/content-selectors.js?v=20260908-content-selectors-1"></script>
<script src="./assets/admin.js?v=20260908-content-selectors-1"></script>
<script src="./assets/admin.js?v=20260918-field-help-escape-1"></script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void exposesDefaultThemeSelectionInUiSettings() throws IOException {
assertTrue(i18n.contains("正在预览所选主题。保存界面设置后,它才会成为默认主题。"));
assertTrue(index.contains("./assets/admin.css?v=20260909-selector-toolbar-1"));
assertTrue(index.contains("/login/assets/ui-i18n.js?v=20260908-content-selectors-1"));
assertTrue(index.contains("./assets/admin.js?v=20260908-content-selectors-1"));
assertTrue(index.contains("./assets/admin.js?v=20260918-field-help-escape-1"));
assertFalse(index.contains("id=\"ui-settings-status\""));
assertFalse(javascript.contains("Default language: ${"));
assertFalse(javascript.contains("setDefaultTheme(themeSelect.value)"));
Expand Down
59 changes: 59 additions & 0 deletions admin-ui/src/test/js/blobstore-credentials.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,62 @@ test('OSS Native forces static keys while file storage does not require S3 crede
assert.equal(context.validateBlobStoreForm(), true);
assert.equal(element('blobstore-secret-key').required, false);
});

test('Escape dismisses credential help before closing the form and preserves unsaved values', () => {
const { context, element } = form();
const listeners = new Map();
const trigger = element('blobstore-credential-help');
const popover = element('field-help-popover');
const modal = element('blobstore-form-modal');
trigger.dataset.tooltip = 'Default AWS credentials';
trigger.addEventListener = (type, listener) => listeners.set(type, listener);
trigger.removeAttribute = () => {};
trigger.getBoundingClientRect = () => ({ left: 100, top: 100, bottom: 118, width: 18 });
popover.hidden = true;
popover.style = {};
popover.addEventListener = () => {};
popover.getBoundingClientRect = () => ({ width: 200, height: 60 });
modal.hidden = false;
modal.dataset.formId = 'blobstore-form';
context.document.querySelectorAll = selector => selector === '.field-help' ? [trigger] : [];
context.document.addEventListener = () => {};
context.window = { innerWidth: 1200, innerHeight: 800, addEventListener() {} };
context.clearTimeout = clearTimeout;
context.activeFormModal = () => modal.hidden ? null : modal;
context.dismissFormModal = formId => {
assert.equal(formId, 'blobstore-form');
modal.hidden = true;
element('blobstore-name').value = '';
};
vm.runInContext(`let activeFieldHelpTrigger = null; let fieldHelpHideTimer = null;\n`
+ source.slice(source.indexOf('function clearFieldHelpHideTimer()'), source.indexOf('function refreshCleanupScheduleFields()'))
+ source.slice(source.indexOf('function handleFormModalKeydown(event)'), source.indexOf('const blobStoreS3RequiredFields')), context);
context.bindFieldHelpTooltips();

function press(key) {
const event = {
key, defaultPrevented: false, propagationStopped: false,
preventDefault() { this.defaultPrevented = true; },
stopPropagation() { this.propagationStopped = true; },
};
listeners.get('keydown')(event);
if (!event.propagationStopped) context.handleFormModalKeydown(event);
return event;
}

element('blobstore-name').value = 'unsaved-store';
listeners.get('focus')();
assert.equal(popover.hidden, false);
assert.equal(press('Enter').defaultPrevented, false);
assert.equal(popover.hidden, false);

const firstEscape = press('Escape');
assert.equal(popover.hidden, true);
assert.equal(modal.hidden, false, 'dismissing the tooltip must leave the form open');
assert.equal(element('blobstore-name').value, 'unsaved-store');
assert.equal(firstEscape.defaultPrevented, true);

const secondEscape = press('Escape');
assert.equal(secondEscape.propagationStopped, false);
assert.equal(modal.hidden, true, 'Escape must still close the form once the tooltip is hidden');
});
Loading