Skip to content

Commit 908fba1

Browse files
fix(ui): prevent slug field error tooltip from hiding lock button (#15885)
Moves the slug field's `Generate` / `Lock` / `Unlock` buttons inside the input (via `TextInput`'s `AfterInput` slot) so the validation error tooltip - which renders above the field - no longer covers them and locks the user out of unlocking the input. The input reserves space with `padding-inline-end` so long values don't run under the buttons. Fixes #14827. #### Before <img width="1079" height="127" alt="before" src="https://github.com/user-attachments/assets/e4184308-7a92-421b-b9a1-76470e9b8132" /> ### After <img width="652" height="195" alt="Screenshot 2026-07-07 at 10 43 14 AM" src="https://github.com/user-attachments/assets/e4a2d1b9-f0fc-4d5a-933d-48ac1e082bff" /> <img width="647" height="197" alt="Screenshot 2026-07-07 at 10 43 31 AM" src="https://github.com/user-attachments/assets/d0fbaf69-06ba-43c9-b3a9-85488c7347e5" /> ##### With long text <img width="420" height="202" alt="Screenshot 2026-07-07 at 11 53 47 AM" src="https://github.com/user-attachments/assets/3c2b8707-a6b1-43cb-82d1-ac14f71d9806" /> --------- Co-authored-by: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com>
1 parent f23b693 commit 908fba1

3 files changed

Lines changed: 64 additions & 12 deletions

File tree

packages/ui/src/fields/Slug/index.scss

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,25 @@
99
gap: calc(var(--base) / 2);
1010
}
1111

12+
// Buttons sit inside the input, pinned to the right.
13+
&__actions {
14+
position: absolute;
15+
top: 0;
16+
bottom: 0;
17+
inset-inline-end: calc(var(--base) / 2);
18+
display: flex;
19+
align-items: center;
20+
gap: calc(var(--base) / 2);
21+
}
22+
23+
// Reserve room for the buttons so long values never run under them.
24+
.field-type.text:not(.has-many) input {
25+
padding-inline-end: calc(var(--base) * 6);
26+
}
27+
1228
.lock-button {
1329
margin: 0;
14-
padding-bottom: 0.3125rem;
30+
padding: 0;
1531
}
1632
}
1733
}

packages/ui/src/fields/Slug/index.tsx

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const SlugField: React.FC<SlugFieldClientProps> = ({ field, path, useAsSl
2727

2828
const { slugify } = useServerFunctions()
2929

30-
const { setValue, value } = useField<string>({ path: path || field.name })
30+
const { setValue, showError, value } = useField<string>({ path: path || field.name })
3131

3232
const { getData, getDataByPath } = useForm()
3333

@@ -75,21 +75,36 @@ export const SlugField: React.FC<SlugFieldClientProps> = ({ field, path, useAsSl
7575
<div className="field-type slug-field-component">
7676
<div className="label-wrapper">
7777
<FieldLabel htmlFor={`field-${path}`} label={label} />
78-
{!readOnlyFromProps && !isLocked && (
79-
<Button buttonStyle="none" className="lock-button" onClick={handleGenerate}>
80-
{t('authentication:generate')}
81-
</Button>
82-
)}
83-
{!readOnlyFromProps && (
84-
<Button buttonStyle="none" className="lock-button" onClick={toggleLock}>
85-
{isLocked ? t('general:unlock') : t('general:lock')}
86-
</Button>
87-
)}
8878
</div>
8979
<TextInput
80+
AfterInput={
81+
readOnlyFromProps ? undefined : (
82+
<div className="slug-field-component__actions">
83+
{!isLocked && (
84+
<Button
85+
buttonStyle="none"
86+
className="lock-button"
87+
id={`field-${path}-generate`}
88+
onClick={handleGenerate}
89+
>
90+
{t('authentication:generate')}
91+
</Button>
92+
)}
93+
<Button
94+
buttonStyle="none"
95+
className="lock-button"
96+
id={`field-${path}-lock`}
97+
onClick={toggleLock}
98+
>
99+
{isLocked ? t('general:unlock') : t('general:lock')}
100+
</Button>
101+
</div>
102+
)
103+
}
90104
onChange={setValue}
91105
path={path || field.name}
92106
readOnly={Boolean(readOnlyFromProps || isLocked)}
107+
showError={showError}
93108
value={value}
94109
/>
95110
</div>

test/fields/collections/SlugField/e2e.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,27 @@ describe('SlugField', () => {
186186
})
187187
})
188188

189+
describe('validation error', () => {
190+
test('should keep the lock button clickable when the slug field has a validation error', async () => {
191+
await page.goto(url.create)
192+
await page.locator('#field-title').waitFor()
193+
194+
const slugFieldComponent = page
195+
.locator('.slug-field-component')
196+
.filter({ has: page.locator('#field-slug') })
197+
198+
// Save without filling required fields to surface validation errors
199+
await page.locator('#action-save').click()
200+
201+
// The slug field error tooltip should render
202+
await expect(slugFieldComponent.locator('.field-error')).toBeVisible()
203+
204+
await slugFieldComponent.locator('.lock-button').click()
205+
206+
await expect(page.locator('#field-slug')).toBeEnabled()
207+
})
208+
})
209+
189210
describe('A11y', () => {
190211
test('Edit view should have no accessibility violations', async ({}, testInfo) => {
191212
await page.goto(url.create)

0 commit comments

Comments
 (0)