mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-06-19 08:23:45 +08:00
The `required: true` validation on upload fields in form templates was
not being enforced. Users could submit forms without attaching any file
even when the upload field was marked as required.
The root cause is that the upload field used a `<input type="hidden">`
to store its value. Hidden inputs are excluded from the browser's
constraint validation API — `form.checkValidity()` skips them entirely.
All other form template field types (input, textarea, dropdown, etc.)
use visible form elements where the native `required` attribute works.
The fix changes the hidden input to a visually-hidden `<input
type="text">` so that native constraint validation applies. The input is
positioned off-screen via CSS and made non-interactive with
`tabindex="-1"` and `aria-hidden="true"`. It is also placed as the last
child in the container so the validation error message (inserted via
`insertAdjacentElement("afterend")`) renders in a visible location.
A synthetic `input` event is dispatched after upload completion so the
validation error clears once a file is attached.
https://meta.discourse.org/t/398972
88 lines
1.7 KiB
SCSS
Vendored
88 lines
1.7 KiB
SCSS
Vendored
.form-template-field {
|
|
position: relative;
|
|
|
|
input,
|
|
textarea,
|
|
select {
|
|
width: 100%;
|
|
}
|
|
|
|
&__dropdown {
|
|
border: 1px solid var(--primary-medium);
|
|
border-radius: var(--d-input-border-radius);
|
|
padding: 0.5em 0.65em;
|
|
font-size: var(--font-0);
|
|
}
|
|
|
|
&__multi-select {
|
|
border: 1px solid var(--primary-medium);
|
|
border-radius: var(--d-input-border-radius);
|
|
padding: 0.5em 0.65em;
|
|
font-size: var(--font-0);
|
|
}
|
|
|
|
// END of what should be removed after updating dropdown/multi-select to use select-kit
|
|
&__required-indicator {
|
|
color: var(--danger);
|
|
margin-left: 0.5em;
|
|
font-size: var(--font-down-4);
|
|
}
|
|
|
|
&__uploaded-files {
|
|
list-style: none;
|
|
margin-left: 0;
|
|
|
|
li {
|
|
padding: 0.5rem;
|
|
margin-block: 0.25rem;
|
|
border: 1px solid var(--primary-low-mid);
|
|
background: var(--primary-low);
|
|
border-radius: var(--d-border-radius);
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
a {
|
|
@include ellipsis;
|
|
width: 70%;
|
|
}
|
|
|
|
span {
|
|
color: var(--primary-high);
|
|
margin-left: auto;
|
|
font-size: var(--font-down-1);
|
|
}
|
|
}
|
|
|
|
.d-icon {
|
|
color: var(--tertiary);
|
|
margin-right: 0.5rem;
|
|
}
|
|
}
|
|
|
|
&__textarea {
|
|
min-height: 100px;
|
|
}
|
|
|
|
&__description {
|
|
display: inline-block;
|
|
margin-bottom: 0.25rem;
|
|
font-size: var(--font-down-1);
|
|
color: var(--primary-medium);
|
|
}
|
|
|
|
&__upload-hidden-input {
|
|
position: absolute;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
&__composer {
|
|
// same default effective height (150px) than a text control
|
|
height: 189px;
|
|
width: 100%;
|
|
|
|
.d-editor-preview-wrapper {
|
|
display: none;
|
|
}
|
|
}
|
|
}
|