+
+
+
+
diff --git a/app/views/install/installer/css/styles.css b/app/views/install/installer/css/styles.css
index 86955395f8..9fa320b3b4 100644
--- a/app/views/install/installer/css/styles.css
+++ b/app/views/install/installer/css/styles.css
@@ -1222,23 +1222,47 @@ body {
display: flex;
align-items: flex-start;
gap: var(--space-3);
- margin-top: var(--space-2);
color: var(--fgcolor-error);
max-height: 0;
opacity: 0;
overflow: hidden;
- padding-inline-start: var(--space-2);
+ /*padding-inline-start: var(--space-2);*/
transition: max-height var(--duration-extended) var(--ease-standard),
opacity var(--duration-extended) var(--ease-standard);
}
-.field-error-text {
- line-height: 1.4;
+.field-error-icon {
+ display: flex;
+ flex-shrink: 0;
+ align-items: center;
+}
+
+.field-error-icon svg {
+ width: 1rem;
+ height: 1rem;
}
.field-error.is-visible {
- max-height: 64px;
opacity: 1;
+ max-height: 64px;
+}
+
+.field-helper {
+ display: flex;
+ align-items: flex-start;
+ gap: var(--space-3);
+ color: var(--fgcolor-neutral-secondary);
+}
+
+.field-helper-icon {
+ display: flex;
+ flex-shrink: 0;
+ align-items: center;
+}
+
+.field-helper-icon svg {
+ width: 1rem;
+ height: 1rem;
}
.input-field {
diff --git a/app/views/install/installer/js/modules/ui.js b/app/views/install/installer/js/modules/ui.js
index 9db7488efb..19aa3b63a0 100644
--- a/app/views/install/installer/js/modules/ui.js
+++ b/app/views/install/installer/js/modules/ui.js
@@ -4,10 +4,15 @@
const clearFieldErrors = (root) => {
if (!root) return;
- root.querySelectorAll('.field-error').forEach((node) => node.remove());
+ root.querySelectorAll('.field-error').forEach((node) => {
+ node.classList.remove('is-visible');
+ });
root.querySelectorAll('.input-field.is-error, .input-action.is-error').forEach((node) => {
node.classList.remove('is-error');
});
+ root.querySelectorAll('.field-helper').forEach((helper) => {
+ helper.style.display = '';
+ });
};
const setFieldError = (input, message) => {
@@ -18,35 +23,39 @@
let errorText = error?.querySelector('.field-error-text');
const hasSameMessage = Boolean(errorText && errorText.textContent === message);
const alreadyVisible = Boolean(error && error.classList.contains('is-visible'));
+
+ if (hasSameMessage && alreadyVisible) {
+ return;
+ }
+
if (!error) {
const template = document.getElementById('field-error-template');
if (template && template.content) {
const fragment = template.content.cloneNode(true);
error = fragment.querySelector('.field-error');
group.appendChild(fragment);
- } else {
- error = document.createElement('div');
- error.className = 'field-error typography-text-xs-400 text-error';
- const text = document.createElement('span');
- text.className = 'field-error-text';
- error.appendChild(text);
- group.appendChild(error);
}
- errorText = error.querySelector('.field-error-text');
+ errorText = error?.querySelector('.field-error-text');
}
if (errorText) {
errorText.textContent = message;
}
- if (!hasSameMessage || !alreadyVisible) {
+
+ if (!alreadyVisible) {
requestAnimationFrame(() => {
error.classList.add('is-visible');
});
}
+
input.classList.add('is-error');
const actionWrapper = input.closest('.input-action');
if (actionWrapper) {
actionWrapper.classList.add('is-error');
}
+ const helper = group.querySelector('.field-helper');
+ if (helper) {
+ helper.style.display = 'none';
+ }
};
const bindErrorClear = (input) => {
@@ -56,15 +65,16 @@
const error = group?.querySelector('.field-error');
if (error) {
error.classList.remove('is-visible');
- setTimeout(() => {
- error.remove();
- }, TIMINGS?.errorClear ?? 0);
}
input.classList.remove('is-error');
const actionWrapper = input.closest('.input-action');
if (actionWrapper) {
actionWrapper.classList.remove('is-error');
}
+ const helper = group?.querySelector('.field-helper');
+ if (helper) {
+ helper.style.display = '';
+ }
};
input.addEventListener('input', handler);
input.addEventListener('change', handler);
diff --git a/app/views/install/installer/js/steps.js b/app/views/install/installer/js/steps.js
index 66b95e31eb..bbdb79a941 100644
--- a/app/views/install/installer/js/steps.js
+++ b/app/views/install/installer/js/steps.js
@@ -434,12 +434,12 @@
const password = root?.querySelector('#account-password');
if (!name || !name.value.trim()) {
- setFieldError?.(name, 'Please enter a name');
+ setFieldError?.(name, 'This field is required');
valid = false;
}
if (!email || !email.value.trim()) {
- setFieldError?.(email, 'Please enter an email address');
+ setFieldError?.(email, 'This field is required');
valid = false;
} else if (!isValidEmail?.(email.value.trim())) {
setFieldError?.(email, 'Please enter a valid email address');
@@ -448,10 +448,10 @@
const passwordValue = password?.value ?? '';
if (!password || !/\S/.test(passwordValue)) {
- setFieldError?.(password, 'Please enter a password');
+ setFieldError?.(password, 'This field is required');
valid = false;
} else if (!isValidPassword?.(passwordValue)) {
- setFieldError?.(password, 'Password must be at least 8 characters');
+ setFieldError?.(password, 'Password must be at least 8 characters long');
valid = false;
}
diff --git a/app/views/install/installer/templates/steps/step-3.phtml b/app/views/install/installer/templates/steps/step-3.phtml
index 22b2f830ad..7ad151a1fb 100644
--- a/app/views/install/installer/templates/steps/step-3.phtml
+++ b/app/views/install/installer/templates/steps/step-3.phtml
@@ -49,7 +49,7 @@ $accountNameValue = htmlspecialchars($defaultAccountName, ENT_QUOTES, 'UTF-8');
id="account-password"
name="accountPassword"
class="input-action-input typography-text-m-400 text-neutral-primary"
- placeholder="Password must be at least 8 characters long"
+ placeholder="Enter password"
autocomplete="new-password"
>
@@ -63,6 +63,12 @@ $accountNameValue = htmlspecialchars($defaultAccountName, ENT_QUOTES, 'UTF-8');
+