feat(components): [input] add password-icon slot (#23772)

* feat(components): [input] add `password-icon` slot

* Update docs/en-US/component/input.md

* docs: simply example

* docs: improve example

---------

Co-authored-by: rzzf <cszhjh@gmail.com>
This commit is contained in:
知晓同丶
2026-03-11 12:12:13 +08:00
committed by GitHub
parent 1a484aac7b
commit 3387031d21
4 changed files with 74 additions and 16 deletions

View File

@@ -1,4 +1,4 @@
---
---
title: Input
lang: en-US
---
@@ -51,7 +51,7 @@ input/formatter
## Password box
:::demo Make a toggle-able password Input with the `show-password` attribute.
:::demo Make a toggle-able password Input with the `show-password` attribute. Since ^(2.13.6), the `password-icon` slot is supported to override the default icon.
input/password
@@ -172,12 +172,13 @@ input/length-limiting
### Slots
| Name | Description |
| ------- | ------------------------------------------------------------------------- |
| prefix | content as Input prefix, only works when `type` is not 'textarea' |
| suffix | content as Input suffix, only works when `type` is not 'textarea' |
| prepend | content to prepend before Input, only works when `type` is not 'textarea' |
| append | content to append after Input, only works when `type` is not 'textarea' |
| Name | Description |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------- |
| prefix | content as Input prefix, only works when `type` is not 'textarea' |
| suffix | content as Input suffix, only works when `type` is not 'textarea' |
| prepend | content to prepend before Input, only works when `type` is not 'textarea' |
| append | content to append after Input, only works when `type` is not 'textarea' |
| password-icon ^(2.13.6) | content as Input password icon, only works when `show-password` is true. The scope variable is `{ visible: boolean }` |
### Exposes

View File

@@ -1,15 +1,40 @@
<template>
<el-input
v-model="input"
style="width: 240px"
type="password"
placeholder="Please input password"
show-password
/>
<div class="input-group">
<el-input
v-model="input"
style="width: 240px"
type="password"
placeholder="Please input password"
show-password
/>
<el-input
v-model="input"
style="width: 240px"
type="password"
placeholder="Please input password"
show-password
>
<template #password-icon="{ visible }">
<el-icon :size="16">
<Unlock v-if="visible" />
<Lock v-else />
</el-icon>
</template>
</el-input>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { Lock, Unlock } from '@element-plus/icons-vue'
const input = ref('')
</script>
<style scoped>
.input-group {
display: flex;
align-items: center;
gap: 1em;
}
</style>

View File

@@ -614,6 +614,36 @@ describe('Input.vue', () => {
expect(input.element.selectionEnd).toBe(4)
})
test('password-icon slot', async () => {
const wrapper = mount(() => (
<Input
modelValue="123"
showPassword
v-slots={{
'password-icon': ({ visible }: { visible: boolean }) => (
<span class="custom-password-icon">
{visible ? 'Hide' : 'Show'}
</span>
),
}}
/>
))
const icon = wrapper.find('.el-input__password')
expect(icon.exists()).toBe(true)
// Initial state: password hidden
expect(wrapper.find('.custom-password-icon').text()).toBe('Show')
// Click to toggle
await icon.trigger('click')
expect(wrapper.find('.custom-password-icon').text()).toBe('Hide')
// Click again
await icon.trigger('click')
expect(wrapper.find('.custom-password-icon').text()).toBe('Show')
})
describe('form item accessibility integration', () => {
test('automatic id attachment', async () => {
const wrapper = mount(() => (

View File

@@ -83,7 +83,9 @@
@mousedown.prevent="NOOP"
@mouseup.prevent="NOOP"
>
<component :is="passwordIcon" />
<slot name="password-icon" :visible="passwordVisible">
<component :is="passwordIcon" />
</slot>
</el-icon>
<span
v-if="isWordLimitVisible"