refactor(webapp): migrate ds-input to OcelotInput (#9428)

This commit is contained in:
Ulf Gebhardt 2026-03-23 15:12:57 +01:00 committed by GitHub
parent 9a92e2209f
commit 7eb1bbdb3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 374 additions and 77 deletions

View File

@ -15,7 +15,7 @@ Phase 4: Tier 1 ██████████ 100% (OsButton, OsIcon, Os
Phase 4: Tier A → HTML ██████████ 100% (10 ds-* Wrapper → Plain HTML) ✅
Phase 4: Tier B ██████████ 100% (ds-chip→OsBadge✅, ds-tag→OsBadge✅, ds-grid✅, ds-number→OsNumber✅, ds-radio→HTML✅)
Phase 4: Tier B ██████████ 100% (Chip→OsBadge, Tag→OsBadge, Grid→HTML, Number→OsNumber, Radio→HTML, Table→HTML) ✅
Phase 4: Tier 2+ ██████░░░░ 50% (OsModal✅, ds-form entkoppelt✅) | Rest ausstehend (OsInput, OsMenu, OsSelect, OsDropdown, OsAvatar)
Phase 4: Tier 2+ ████████░░ 60% (OsModal✅, ds-form entkoppelt✅, ds-input→OcelotInput✅) | Rest ausstehend (OsMenu, OsSelect, OsDropdown, OsAvatar)
```
### Statistiken
@ -29,8 +29,10 @@ Phase 4: Tier 2+ ██████░░░░ 50% (OsModal✅, ds-form
| ✅ → OsBadge | Chip (20 Nutzungen → OsBadge), Tag (3 → OsBadge shape="square") |
| ✅ → OsNumber | Number (5 Nutzungen → OsNumber, CountTo.vue gelöscht, vue-count-to entfernt) |
| ✅ → Plain HTML | Radio (1 Datei → native `<input type="radio">` in ReportModal) |
| ⬜ → UI-Library | Modal, Input, Menu, MenuItem, Select (5) — Tier 2-3 |
| ✅ ds-form entkoppelt | Form-Validierung → formValidation Mixin (async-validator), ds-input/ds-select bleiben als UI-Komponenten |
| ✅ → OsModal | Modal (7 Nutzungen → OsModal, Focus-Trap, Scroll-Lock, A11y) |
| ✅ ds-input → OcelotInput | Input (23 Dateien → OcelotInput Webapp-Komponente, lokale Imports, formValidation-kompatibel) |
| ✅ ds-form entkoppelt | Form-Validierung → formValidation Mixin (async-validator), vuelidate entfernt |
| ⬜ → UI-Library | Menu, MenuItem, Select (3) — Tier 2-3 |
| ⬜ Nicht in Webapp | Code, CopyField, FormItem, InputError, InputLabel, Page, PageTitle, Logo, Avatar, TableCol, TableHeadCol (11) |
### OsButton Migration (Phase 3) ✅
@ -74,9 +76,9 @@ Phase 4: Tier 2+ ██████░░░░ 50% (OsModal✅, ds-form
| 14 | CopyField | ⬜ Nicht genutzt | Nicht in Webapp verwendet |
| 15 | Form | ⬜ Offen | 18 Dateien — HTML `<form>` oder OsForm? |
| 16 | FormItem | ⬜ Nicht genutzt | Nicht in Webapp verwendet |
| 17 | Input | ⬜ Tier 2 | 23 Dateien → OsInput (gekoppelt mit Form) |
| 18 | InputError | ⬜ Nicht genutzt | Intern von Input genutzt |
| 19 | InputLabel | ⬜ Nicht genutzt | Intern von Input genutzt |
| 17 | Input | ✅ → OcelotInput | 23 Dateien → OcelotInput (Webapp-Komponente, FormItem/Label/Error vereint) |
| 18 | InputError | ✅ → OcelotInput | In OcelotInput integriert |
| 19 | InputLabel | ✅ → OcelotInput | In OcelotInput integriert |
| 20 | Radio | ✅ → HTML | 1 Datei → native `<input type="radio">` + `<fieldset>` (ReportModal) |
| 21 | Select | ⬜ Tier 4 | 3 Dateien → OsSelect |
@ -367,7 +369,7 @@ Phase 4: Tier 2+ ██████░░░░ 50% (OsModal✅, ds-form
### Basis-Komponenten — UI-Library (ausstehend)
- Modal → OsModal ✅
- Input → OsInput
- Input → OcelotInput (Webapp-Komponente) ✅ — langfristig → OsInput in packages/ui
- Select → OsSelect
- Avatar → OsAvatar (falls benötigt)
@ -409,6 +411,7 @@ Phase 4: Tier 2+ ██████░░░░ 50% (OsModal✅, ds-form
| 2026-02-11→18 | Claude | **Sessions 12-26** | OsButton M4c abgeschlossen, OsIcon, OsSpinner, OsCard implementiert + Webapp-Migration, BaseButton/BaseCard/BaseIcon/LoadingSpinner gelöscht |
| 2026-02-19 | Claude | **Tier A Migration** | 10 ds-* Vue-Wrapper → Plain HTML + CSS, _ds-compat.scss, ~450 Nutzungen in ~90 Dateien |
| 2026-02-19 | Claude | **Katalog konsolidiert** | Styleguide- und Webapp-Tabellen aktualisiert, veraltete Status korrigiert |
| 2026-03-23 | Claude | **ds-input → OcelotInput** | 23 Dateien migriert, Webapp-Komponente mit lokalen Imports (tree-shakeable), FormItem/Label/Error vereint |
---
@ -445,13 +448,13 @@ Phase 4: Tier 2+ ██████░░░░ 50% (OsModal✅, ds-form
### Phase 4: Tier 2-4 — UI-Library
19. [x] OsModal (h() Render, Focus-Trap, Scroll-Lock, A11y; ConfirmModal + ReportModal nutzen OsModal; DeleteUserModal/DisableModal/ReleaseModal gelöscht) ✅
20. [x] ds-form → formValidation Mixin (async-validator), 18 Dateien migriert, vuelidate entfernt ✅
21. [ ] OsInput (23 Dateien)
21. [x] ds-input → OcelotInput (23 Dateien, Webapp-Komponente mit lokalen Imports, FormItem/Label/Error vereint, formValidation-kompatibel) ✅
22. [ ] OsMenu / OsMenuItem (17 Dateien)
23. [ ] OsSelect (3 Dateien), OsTable (7 Dateien)
---
**✅ Phase 0-3 abgeschlossen. Phase 4: Tier 1 + Tier A ✅, Tier B ✅ (Chip→OsBadge, Tag→OsBadge, Grid→HTML, Number→OsNumber, Radio→HTML, Table→HTML), Tier 2: OsModal ✅, ds-form entkoppelt ✅, Rest ausstehend (OsInput, OsMenu, OsSelect).**
**✅ Phase 0-3 abgeschlossen. Phase 4: Tier 1 + Tier A ✅, Tier B ✅ (Chip→OsBadge, Tag→OsBadge, Grid→HTML, Number→OsNumber, Radio→HTML, Table→HTML), Tier 2: OsModal ✅, ds-form entkoppelt ✅, ds-input → OcelotInput ✅, Rest ausstehend (OsMenu, OsSelect).**
---

View File

@ -81,10 +81,10 @@ Phase 0: ██████████ 100% (6/6 Aufgaben) ✅
Phase 1: ██████████ 100% (6/6 Aufgaben) ✅
Phase 2: ██████████ 100% (26/26 Aufgaben) ✅
Phase 3: ██████████ 100% (24/24 Aufgaben) ✅ - Webapp-Integration komplett
Phase 4: ███████░░░ 70% (19/27 Aufgaben) - Tier 1 ✅, Tier A ✅, Infra ✅, OsBadge ✅, ds-grid ✅, ds-table→HTML ✅, OsNumber ✅, OsModal ✅, ds-radio→HTML ✅ | Tier B ✅, Tier 2-3 ausstehend
Phase 4: ████████░░ 74% (20/27 Aufgaben) - Tier 1 ✅, Tier A ✅, Infra ✅, OsBadge ✅, ds-grid ✅, ds-table→HTML ✅, OsNumber ✅, OsModal ✅, ds-radio→HTML ✅ | Tier B ✅, OcelotInput ✅, Tier 2-3 Rest ausstehend
Phase 5: ░░░░░░░░░░ 0% (0/7 Aufgaben)
───────────────────────────────────────
Gesamt: ████████░░ 84% (81/96 Aufgaben)
Gesamt: ████████░░ 85% (82/96 Aufgaben)
```
### Katalogisierung (Details in KATALOG.md)
@ -289,7 +289,7 @@ ds-chip + ds-tag → OsBadge (UI-Library): ✅
- [x] 0 Tier-A `ds-*` Komponenten-Tags verbleibend
**Verbleibende ds-* Komponenten (6 Typen):**
- Tier C (→ UI-Library): ds-input (23), ds-modal (7→✅ OsModal), ds-menu/ds-menu-item (17), ds-select (3)
- Tier C (→ UI-Library): ds-modal (7→✅ OsModal), ds-input (23→✅ OcelotInput), ds-menu/ds-menu-item (17), ds-select (3)
- ✅ ds-form (18 Dateien) → formValidation Mixin (async-validator), vuelidate entfernt
**Zuvor abgeschlossen (Session 26 - CodeRabbit Review Fixes):**
@ -415,9 +415,9 @@ ds-chip + ds-tag → OsBadge (UI-Library): ✅
- [x] OsNumber Komponente + ds-number/CountTo → OsNumber Webapp-Migration ✅
- [ ] Tier B (Rest): ds-radio → Plain HTML
- [x] OsModal Komponente + DsModal/ConfirmModal/ReportModal → OsModal Webapp-Integration ✅
- [ ] Weitere Tier 2 Komponenten (OsDropdown, OsAvatar, OsInput)
- [ ] Weitere Tier 2 Komponenten (OsDropdown, OsAvatar)
- [x] ds-form → formValidation Mixin (async-validator), 18 Dateien migriert, vuelidate entfernt ✅
- [ ] ds-input → OsInput (23 Dateien, ds-form Kopplung aufgelöst)
- [x] ds-input → OcelotInput (23 Dateien, Webapp-Komponente mit lokalen Imports, formValidation-kompatibel) ✅
- [ ] ds-menu / ds-menu-item → OsMenu / OsMenuItem
- [ ] ds-select → OsSelect
- [ ] Browser-Fehler untersuchen: `TypeError: Cannot read properties of undefined (reading 'heartO')` (ocelotIcons undefined im Browser trotz korrekter Webpack-Aliase)
@ -694,7 +694,7 @@ Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Activ
- [x] OsModal (Basis: DsModal → h() Render-Function, Vue 2/3 Compat, Focus-Trap, Scroll-Lock, A11y) ✅
- [ ] OsDropdown (Basis: Webapp Dropdown)
- [ ] OsAvatar (vereint DsAvatar + ProfileAvatar)
- [ ] OsInput (Basis: DsInput, 23 Dateien — ds-form Kopplung aufgelöst via formValidation Mixin)
- [x] ds-input → OcelotInput (23 Dateien, Webapp-Komponente mit lokalen Imports, formValidation-kompatibel) ✅
**Tier 3: Navigation (UI-Library)**
- [ ] OsMenu (Basis: DsMenu, 11 Dateien)
@ -1852,6 +1852,7 @@ Bei der Migration werden:
| 2026-03-14 | **ds-form entkoppelt** | Neues `formValidation` Mixin (async-validator): provide/subscribe Pattern, formData/formSchema/formErrors, handleInput/handleInputValid Callbacks; vuelidate komplett entfernt |
| 2026-03-14 | **18 Formulare migriert** | CommentForm, ContributionForm, EnterNonce, GroupForm, Password/Change, PasswordReset (2), Registration (5), Signup, MySomethingList, donations, admin/users, settings (3) |
| 2026-03-20 | **formValidation Fix** | `handleInput()` vor `$validateForm()` aufrufen (Reihenfolge-Bug: handleInput überschrieb handleInputValid bei synchronem async-validator Callback) |
| 2026-03-23 | **ds-input → OcelotInput** | Neue Webapp-Komponente `OcelotInput.vue`: vereint DsInput + FormItem + InputLabel + InputError in einer Datei. 23 Vue-Dateien migriert mit lokalen Imports (tree-shakeable). formValidation Mixin voll kompatibel. dot-prop Abhängigkeit durch inline `getNestedValue()` ersetzt. 28 Test-Suites, 210 Tests ✅, 7 Snapshots aktualisiert. |
---
@ -1872,8 +1873,9 @@ Bei der Migration werden:
| ✅ UI-Library | OsButton, OsIcon, OsSpinner, OsCard, OsBadge, OsNumber, OsModal (7) |
| ✅ → Plain HTML | Section, Placeholder, List, ListItem, Container, Heading, Text, Space, Flex, FlexItem, Grid, GridItem, Table, Radio (14) — Tier A/B |
| ✅ → UI-Library | Chip, Tag → OsBadge (2), Number → OsNumber (1) — Tier B |
| ✅ ds-form entkoppelt | Form-Validierung → formValidation Mixin (async-validator), vuelidate entfernt, ds-input/ds-select bleiben als UI-Komponenten |
| ⬜ → UI-Library | Input, Menu, MenuItem, Select (4) — Tier 2-3 |
| ✅ ds-form entkoppelt | Form-Validierung → formValidation Mixin (async-validator), vuelidate entfernt |
| ✅ ds-input → OcelotInput | Webapp-Komponente (23 Dateien), lokale Imports, FormItem/InputLabel/InputError vereint |
| ⬜ → UI-Library | Menu, MenuItem, Select (3) — Tier 2-3 |
| ⬜ Nicht genutzt | Code, CopyField, FormItem, InputError, InputLabel, Page, PageTitle, Logo, Avatar, TableCol, TableHeadCol (11) |
---

View File

@ -26,7 +26,7 @@
</page-params-link>
</div>
<div class="ds-mt-base ds-mb-large"></div>
<ds-input
<ocelot-input
model="title"
:placeholder="$t('contribution.title')"
name="title"
@ -114,7 +114,7 @@
</div>
<div class="ds-grid event-location-grid">
<div class="event-grid-item">
<ds-input
<ocelot-input
model="eventVenue"
name="eventVenue"
:placeholder="$t('post.viewEvent.eventVenue')"
@ -131,7 +131,7 @@
</div>
</div>
<div v-if="showEventLocationName" class="event-grid-item">
<ds-input
<ocelot-input
model="eventLocationName"
name="eventLocationName"
:placeholder="$t('post.viewEvent.eventLocationName')"
@ -233,6 +233,7 @@ import DatePicker from 'vue2-datepicker'
import 'vue2-datepicker/scss/index.scss'
import GetCategories from '~/mixins/getCategoriesMixin.js'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
mixins: [GetCategories, formValidation],
@ -246,6 +247,7 @@ export default {
OsCard,
OsIcon,
PageParamsLink,
OcelotInput,
},
props: {
contribution: {

View File

@ -7,7 +7,7 @@
<label>
{{ $t('settings.deleteUserAccount.pleaseConfirm', { confirm: currentUser.name }) }}
</label>
<ds-input v-model="enableDeletionValue" />
<ocelot-input v-model="enableDeletionValue" />
<p v-show="enableDeletionValue" class="notice">
{{ $t('settings.deleteUserAccount.accountDescription') }}
</p>
@ -64,10 +64,11 @@ import { iconRegistry } from '~/utils/iconRegistry'
import { mapActions, mapGetters } from 'vuex'
import gql from 'graphql-tag'
import { currentUserCountQuery } from '~/graphql/User'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
name: 'DeleteData',
components: { OsButton, OsCard, OsIcon },
components: { OsButton, OsCard, OsIcon, OcelotInput },
data() {
return {
deleteContributions: false,

View File

@ -12,7 +12,7 @@
:select-item="selectItem"
/>
<div v-show="isLinkInputActive" ref="linkInput">
<ds-input
<ocelot-input
id="linkInputId"
v-model="linkUrl"
class="editor-menu-link-input"
@ -40,6 +40,7 @@ import Mention from './nodes/Mention'
import MenuBar from './MenuBar'
import ContextMenu from './ContextMenu'
import SuggestionList from './SuggestionList'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
let throttleInputEvent
@ -49,6 +50,7 @@ export default {
EditorContent,
MenuBar,
SuggestionList,
OcelotInput,
},
props: {
users: { type: Array, default: () => null }, // If 'null', than the Mention extention is not assigned.

View File

@ -1,6 +1,6 @@
<template>
<form class="enter-nonce" @submit.prevent="onSubmit" novalidate>
<ds-input
<ocelot-input
:placeholder="$t('components.registration.email-nonce.form.nonce')"
model="nonce"
name="nonce"
@ -30,11 +30,12 @@
import { OsButton } from '@ocelot-social/ui'
import registrationConstants from '~/constants/registration'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
name: 'EnterNonce',
mixins: [formValidation],
components: { OsButton },
components: { OsButton, OcelotInput },
props: {
email: { type: String, required: true },
},

View File

@ -3,7 +3,7 @@
<form class="group-form" @submit.prevent="onSubmit" novalidate>
<template>
<!-- group Name -->
<ds-input
<ocelot-input
name="name"
:label="$t('group.name')"
model="name"
@ -20,13 +20,13 @@
</os-badge>
<!-- group Slug -->
<ds-input
<ocelot-input
v-if="update"
:label="$t('group.labelSlug')"
model="slug"
icon="at"
:placeholder="`${$t('group.labelSlug')} …`"
></ds-input>
></ocelot-input>
<div v-if="update" class="ds-mt-small ds-mb-large"></div>
@ -61,7 +61,7 @@
</os-badge>
<!-- goal -->
<ds-input
<ocelot-input
name="about"
:label="$t('group.goal')"
v-model="formData.about"
@ -176,6 +176,7 @@ import ActionRadiusSelect from '~/components/Select/ActionRadiusSelect'
import LocationSelect from '~/components/Select/LocationSelect'
import GetCategories from '~/mixins/getCategoriesMixin.js'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
name: 'GroupForm',
@ -188,6 +189,7 @@ export default {
OsBadge,
OsButton,
OsIcon,
OcelotInput,
},
props: {
update: {

View File

@ -13,7 +13,7 @@
<section class="content-column">
<h2 class="title">{{ $t('login.login') }}</h2>
<form :disabled="pending" @submit.prevent="onSubmit">
<ds-input
<ocelot-input
v-model="form.email"
:disabled="pending"
:placeholder="$t('login.email')"
@ -22,7 +22,7 @@
icon="envelope"
/>
<div class="password-wrapper">
<ds-input
<ocelot-input
v-model="form.password"
:disabled="pending"
:placeholder="$t('login.password')"
@ -71,6 +71,7 @@ import ShowPassword from '../ShowPassword/ShowPassword.vue'
import { OsButton, OsCard, OsIcon } from '@ocelot-social/ui'
import { iconRegistry } from '~/utils/iconRegistry'
import { mapGetters, mapMutations } from 'vuex'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
components: {
@ -81,6 +82,7 @@ export default {
OsIcon,
PageParamsLink,
ShowPassword,
OcelotInput,
},
data() {
return {

View File

@ -29,7 +29,7 @@
<span class="report-radio-option-label">{{ option.label }}</span>
</div>
</fieldset>
<ds-input
<ocelot-input
class="reason-description"
v-model="form.reasonDescription"
:schema="formSchema.reasonDescription"
@ -73,6 +73,7 @@ import { SweetalertIcon } from 'vue-sweetalert-icons'
import { reportMutation } from '~/graphql/Moderation.js'
import { valuesReasonCategoryOptions } from '~/constants/modals.js'
import validReport from '~/components/utils/ReportModal'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
name: 'ReportModal',
@ -81,6 +82,7 @@ export default {
OsIcon,
OsModal,
SweetalertIcon,
OcelotInput,
},
props: {
name: { type: String, default: '' },

View File

@ -0,0 +1,210 @@
<template>
<div class="ds-form-item" :class="stateClasses">
<label class="ds-input-label" v-show="!!label" :for="id">
{{ label }}
</label>
<div class="ds-input-wrap">
<div v-if="icon" class="ds-input-icon">
<ds-icon :name="icon" />
</div>
<component
class="ds-input"
:class="[icon && 'ds-input-has-icon', iconRight && 'ds-input-has-icon-right']"
:id="id"
:name="name ? name : model"
:type="type"
:autofocus="autofocus"
:placeholder="placeholder"
:tabindex="tabindex"
:disabled="disabled"
:readonly="readonly"
:is="tag"
:value.prop="innerValue"
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
:rows="type === 'textarea' ? rows : null"
/>
<div v-if="iconRight" class="ds-input-icon-right">
<ds-icon :name="iconRight" />
</div>
</div>
<transition name="ds-input-error">
<div class="ds-input-error" v-show="!!error">
{{ error }}
</div>
</transition>
</div>
</template>
<script>
import Schema from 'async-validator'
Schema.warning = function () {}
function getNestedValue(obj, path) {
return path.split('.').reduce((acc, key) => (acc != null ? acc[key] : undefined), obj)
}
export default {
name: 'OcelotInput',
inject: {
$parentForm: {
default: null,
},
},
props: {
value: {
type: [String, Object, Number, Array],
default: null,
},
model: {
type: String,
default: null,
},
name: {
type: String,
default: null,
},
label: {
type: String,
default: null,
},
id: {
type: String,
default: null,
},
disabled: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
schema: {
type: Object,
default: () => null,
},
size: {
type: String,
default: 'base',
validator: (value) => /^(small|base|large)$/.test(value),
},
tabindex: {
type: Number,
default: 0,
},
type: {
type: String,
default: 'text',
validator: (value) => /^(url|text|password|email|search|textarea)$/.test(value),
},
placeholder: {
type: String,
default: null,
},
autofocus: {
type: Boolean,
default: false,
},
rows: {
type: [String, Number],
default: 1,
},
icon: {
type: String,
default: null,
},
iconRight: {
type: String,
default: null,
},
},
data() {
return {
innerValue: null,
error: null,
focus: false,
}
},
computed: {
tag() {
if (this.type === 'textarea') {
return 'textarea'
}
return 'input'
},
stateClasses() {
return [
this.size && `ds-input-size-${this.size}`,
this.disabled && 'ds-input-is-disabled',
this.readonly && 'ds-input-is-readonly',
this.error && 'ds-input-has-error',
this.focus && 'ds-input-has-focus',
]
},
},
watch: {
value: {
handler(value) {
this.innerValue = value
},
deep: true,
immediate: true,
},
},
created() {
if (this.$parentForm && this.model) {
this.$parentForm.subscribe(this.handleFormUpdate)
}
},
beforeDestroy() {
if (this.$parentForm && this.model) {
this.$parentForm.unsubscribe(this.handleFormUpdate)
}
},
methods: {
handleInput(event) {
this.input(event.target.value)
},
input(value) {
this.innerValue = value
if (this.$parentForm && this.model) {
this.$parentForm.update(this.model, value)
} else {
this.$emit('input', value)
this.validate(value)
}
},
handleFormUpdate(data, errors) {
this.innerValue = getNestedValue(data, this.model)
this.error = errors ? errors[this.model] : null
},
validate(value) {
if (!this.schema) {
return
}
const validator = new Schema({ input: this.schema })
validator.validate({ input: value }, (errors) => {
if (errors) {
this.error = errors[0].message
} else {
this.error = null
}
})
},
handleFocus() {
this.focus = true
},
handleBlur() {
this.focus = false
},
},
}
</script>
<style lang="scss">
/* Styles inherited from global styleguide CSS (ds-input, ds-form-item classes).
* Once ds-input is fully removed from the styleguide, move the styles here. */
</style>

View File

@ -1,20 +1,20 @@
<template>
<form @submit.prevent="onSubmit" novalidate>
<ds-input
<ocelot-input
id="oldPassword"
model="oldPassword"
type="password"
autocomplete="off"
:label="$t('settings.security.change-password.label-old-password')"
/>
<ds-input
<ocelot-input
id="password"
model="password"
type="password"
autocomplete="off"
:label="$t('settings.security.change-password.label-new-password')"
/>
<ds-input
<ocelot-input
id="passwordConfirmation"
model="passwordConfirmation"
type="password"
@ -46,6 +46,7 @@ import gql from 'graphql-tag'
import PasswordStrength from './Strength'
import PasswordForm from '~/components/utils/PasswordFormHelper'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
name: 'ChangePassword',
@ -54,6 +55,7 @@ export default {
OsButton,
OsIcon,
PasswordStrength,
OcelotInput,
},
created() {
this.icons = iconRegistry

View File

@ -6,14 +6,14 @@
class="change-password"
novalidate
>
<ds-input
<ocelot-input
id="password"
model="password"
type="password"
autocomplete="off"
:label="$t('settings.security.change-password.label-new-password')"
/>
<ds-input
<ocelot-input
id="passwordConfirmation"
model="passwordConfirmation"
type="password"
@ -73,6 +73,7 @@ import gql from 'graphql-tag'
import { SweetalertIcon } from 'vue-sweetalert-icons'
import PasswordForm from '~/components/utils/PasswordFormHelper'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
mixins: [formValidation],
@ -81,6 +82,7 @@ export default {
OsIcon,
SweetalertIcon,
PasswordStrength,
OcelotInput,
},
props: {
email: { type: String, required: true },

View File

@ -1,7 +1,7 @@
<template>
<form v-if="!submitted" @submit.prevent="onSubmit" novalidate>
<div class="ds-my-small">
<ds-input
<ocelot-input
:placeholder="$t('login.email')"
type="email"
id="email"
@ -41,12 +41,14 @@ import { OsButton } from '@ocelot-social/ui'
import gql from 'graphql-tag'
import { SweetalertIcon } from 'vue-sweetalert-icons'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
mixins: [formValidation],
components: {
OsButton,
SweetalertIcon,
OcelotInput,
},
data() {
return {

View File

@ -31,20 +31,20 @@
<div v-if="askForRealName" class="full-name">
<!-- <p>{{ $t('settings.data.realNamePlease') }}</p>-->
<ds-input
<ocelot-input
id="givenName"
model="givenName"
:label="$t('settings.data.givenName')"
:placeholder="$t('settings.data.givenNamePlaceholder')"
/>
<ds-input
<ocelot-input
id="surName"
model="surName"
:label="$t('settings.data.surName')"
:placeholder="$t('settings.data.surNamePlaceholder')"
/>
</div>
<ds-input
<ocelot-input
v-else
id="name"
model="name"
@ -52,7 +52,7 @@
:placeholder="$t('settings.data.namePlaceholder')"
/>
<div class="password-wrapper">
<ds-input
<ocelot-input
id="password"
model="password"
:type="showPassword ? 'text' : 'password'"
@ -68,7 +68,7 @@
/>
</div>
<div class="password-wrapper">
<ds-input
<ocelot-input
id="passwordConfirmation"
model="passwordConfirmation"
:type="showPasswordConfirm ? 'text' : 'password'"
@ -161,6 +161,7 @@ import { iconRegistry } from '~/utils/iconRegistry'
import ShowPassword from '../ShowPassword/ShowPassword.vue'
import LocationSelect from '~/components/Select/LocationSelect'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
const threePerEmSpace = '' // unicode u+2004;
@ -174,6 +175,7 @@ export default {
ShowPassword,
SweetalertIcon,
LocationSelect,
OcelotInput,
},
props: {
sliderData: { type: Object, required: true },

View File

@ -3,7 +3,13 @@
<p class="ds-text">
{{ $t('components.registration.signup.form.description') }}
</p>
<ds-input :placeholder="$t('login.email')" type="email" id="email" model="email" name="email" />
<ocelot-input
:placeholder="$t('login.email')"
type="email"
id="email"
model="email"
name="email"
/>
<slot></slot>
<p class="ds-text" v-if="sliderData.collectedInputData.emailSend">
<input
@ -26,6 +32,7 @@ import metadata from '~/constants/metadata'
import { isEmail } from 'validator'
import translateErrorMessage from '~/components/utils/TranslateErrorMessage'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export const SignupMutation = gql`
mutation ($email: String!, $locale: String!, $inviteCode: String) {
@ -37,6 +44,9 @@ export const SignupMutation = gql`
export default {
name: 'RegistrationSlideEmail',
mixins: [formValidation],
components: {
OcelotInput,
},
props: {
sliderData: { type: Object, required: true },
},

View File

@ -1,6 +1,6 @@
<template>
<form class="enter-invite" @submit.prevent novalidate>
<ds-input
<ocelot-input
:placeholder="formSchema.inviteCode.placeholder"
:minlength="formSchema.inviteCode.minLength"
:maxlength="formSchema.inviteCode.maxLength"
@ -42,6 +42,7 @@ import registrationConstants from '~/constants/registrationBranded.js'
import { validateInviteCode } from '~/graphql/InviteCode'
import ProfileAvatar from '~/components/_new/generic/ProfileAvatar/ProfileAvatar'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
name: 'RegistrationSlideInvite',
@ -51,6 +52,7 @@ export default {
},
components: {
ProfileAvatar,
OcelotInput,
},
data() {
return {

View File

@ -1,7 +1,7 @@
<template>
<form class="enter-nonce" @submit.prevent="onSubmit" novalidate>
<email-display-and-verify :email="sliderData.collectedInputData.email" />
<ds-input
<ocelot-input
:placeholder="$t('components.registration.email-nonce.form.nonce')"
model="nonce"
name="nonce"
@ -22,6 +22,7 @@ import registrationConstants from '~/constants/registration'
import formValidation from '~/mixins/formValidation'
import EmailDisplayAndVerify from './EmailDisplayAndVerify'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export const verifyNonceQuery = gql`
query ($email: String!, $nonce: String!) {
@ -33,6 +34,7 @@ export default {
mixins: [formValidation],
components: {
EmailDisplayAndVerify,
OcelotInput,
},
props: {
sliderData: { type: Object, required: true },

View File

@ -17,7 +17,7 @@
}}
</p>
</div>
<ds-input
<ocelot-input
:placeholder="invitation ? $t('profile.invites.emailPlaceholder') : $t('login.email')"
type="email"
id="email"
@ -62,6 +62,7 @@ import metadata from '~/constants/metadata'
import { SweetalertIcon } from 'vue-sweetalert-icons'
import translateErrorMessage from '~/components/utils/TranslateErrorMessage'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export const SignupMutation = gql`
mutation ($email: String!, $locale: String!, $inviteCode: String) {
@ -76,6 +77,7 @@ export default {
components: {
OsButton,
SweetalertIcon,
OcelotInput,
},
props: {
invitation: { type: Boolean, default: false },

View File

@ -2,7 +2,7 @@
<div class="create-invitation">
<div>{{ $t('invite-codes.generate-code-explanation') }}</div>
<form @submit.prevent="generateInviteCode" class="generate-invite-code-form">
<ds-input
<ocelot-input
name="comment"
:placeholder="$t('invite-codes.comment-placeholder')"
v-model="comment"
@ -28,10 +28,11 @@
<script>
import { OsButton, OsIcon } from '@ocelot-social/ui'
import { iconRegistry } from '~/utils/iconRegistry'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
name: 'CreateInvitation',
components: { OsButton, OsIcon },
components: { OsButton, OsIcon, OcelotInput },
props: {
disabled: {
type: Boolean,

View File

@ -20,13 +20,15 @@ exports[`CreateInvitation.vue renders 1`] = `
style="display: none;"
>
</label>
<div
class="ds-input-wrap"
>
<!---->
<input
class="ds-input"
name="comment"
@ -34,8 +36,10 @@ exports[`CreateInvitation.vue renders 1`] = `
tabindex="0"
type="text"
/>
<!---->
</div>
<transition-stub
name="ds-input-error"
>
@ -44,8 +48,8 @@ exports[`CreateInvitation.vue renders 1`] = `
style="display: none;"
>
</div>
</transition-stub>
</div>
@ -106,13 +110,15 @@ exports[`CreateInvitation.vue renders with disabled button 1`] = `
style="display: none;"
>
</label>
<div
class="ds-input-wrap"
>
<!---->
<input
class="ds-input"
name="comment"
@ -120,8 +126,10 @@ exports[`CreateInvitation.vue renders with disabled button 1`] = `
tabindex="0"
type="text"
/>
<!---->
</div>
<transition-stub
name="ds-input-error"
>
@ -130,8 +138,8 @@ exports[`CreateInvitation.vue renders with disabled button 1`] = `
style="display: none;"
>
</div>
</transition-stub>
</div>

View File

@ -228,13 +228,15 @@ exports[`InvitationList.vue renders 1`] = `
style="display: none;"
>
</label>
<div
class="ds-input-wrap"
>
<!---->
<input
class="ds-input"
name="comment"
@ -242,8 +244,10 @@ exports[`InvitationList.vue renders 1`] = `
tabindex="0"
type="text"
/>
<!---->
</div>
<transition-stub
name="ds-input-error"
>
@ -252,8 +256,8 @@ exports[`InvitationList.vue renders 1`] = `
style="display: none;"
>
</div>
</transition-stub>
</div>
@ -325,13 +329,15 @@ exports[`InvitationList.vue renders empty state 1`] = `
style="display: none;"
>
</label>
<div
class="ds-input-wrap"
>
<!---->
<input
class="ds-input"
name="comment"
@ -339,8 +345,10 @@ exports[`InvitationList.vue renders empty state 1`] = `
tabindex="0"
type="text"
/>
<!---->
</div>
<transition-stub
name="ds-input-error"
>
@ -349,8 +357,8 @@ exports[`InvitationList.vue renders empty state 1`] = `
style="display: none;"
>
</div>
</transition-stub>
</div>

View File

@ -31,7 +31,7 @@
</li>
</ul>
<ds-input
<ocelot-input
v-if="isMoreAsVisible && !hasMore"
:name="uniqueName"
:placeholder="filterPlaceholder"
@ -70,6 +70,7 @@ import { escape } from 'xregexp/xregexp-all.js'
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import UserTeaser from '~/components/UserTeaser/UserTeaser'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export const profileListVisibleCount = 6
const VIRTUAL_SCROLL_THRESHOLD = 50
@ -81,6 +82,7 @@ export default {
OsCard,
UserTeaser,
RecycleScroller,
OcelotInput,
},
props: {
uniqueName: { type: String, required: true },

View File

@ -8,7 +8,7 @@
{{ $t('admin.donations.showDonationsCheckboxLabel') }}
</label>
</p>
<ds-input
<ocelot-input
id="donations-goal"
class="donations-data"
model="goal"
@ -18,7 +18,7 @@
:disabled="!showDonations"
data-test="donations-goal"
/>
<ds-input
<ocelot-input
id="donations-progress"
class="donations-data"
model="progress"
@ -39,10 +39,11 @@
import { OsButton, OsCard } from '@ocelot-social/ui'
import { DonationsQuery, UpdateDonations } from '~/graphql/Donations'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
mixins: [formValidation],
components: { OsButton, OsCard },
components: { OsButton, OsCard, OcelotInput },
data() {
return {
formData: {

View File

@ -30,9 +30,10 @@ exports[`Users given badges are disabled renders 1`] = `
style="display: none;"
>
</label>
<div
class="ds-input-wrap"
>
@ -54,6 +55,7 @@ exports[`Users given badges are disabled renders 1`] = `
</svg>
</span>
</div>
<input
class="ds-input ds-input-has-icon"
name="query"
@ -61,8 +63,10 @@ exports[`Users given badges are disabled renders 1`] = `
tabindex="0"
type="text"
/>
<!---->
</div>
<transition-stub
name="ds-input-error"
>
@ -71,8 +75,8 @@ exports[`Users given badges are disabled renders 1`] = `
style="display: none;"
>
</div>
</transition-stub>
</div>
@ -479,9 +483,10 @@ exports[`Users given badges are enabled renders 1`] = `
style="display: none;"
>
</label>
<div
class="ds-input-wrap"
>
@ -503,6 +508,7 @@ exports[`Users given badges are enabled renders 1`] = `
</svg>
</span>
</div>
<input
class="ds-input ds-input-has-icon"
name="query"
@ -510,8 +516,10 @@ exports[`Users given badges are enabled renders 1`] = `
tabindex="0"
type="text"
/>
<!---->
</div>
<transition-stub
name="ds-input-error"
>
@ -520,8 +528,8 @@ exports[`Users given badges are enabled renders 1`] = `
style="display: none;"
>
</div>
</transition-stub>
</div>

View File

@ -5,7 +5,7 @@
<form @submit.prevent="onSubmit" novalidate>
<div class="ds-flex ds-flex-gap-small">
<div style="flex: 0 0 90%; width: 90%">
<ds-input
<ocelot-input
model="query"
:placeholder="$t('admin.users.form.placeholder')"
icon="search"
@ -163,6 +163,7 @@ import PaginationButtons from '~/components/_new/generic/PaginationButtons/Pagin
import { adminUserQuery } from '~/graphql/User'
import { FetchAllRoles, updateUserRole } from '~/graphql/admin/Roles'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
mixins: [formValidation],
@ -171,6 +172,7 @@ export default {
OsCard,
OsIcon,
PaginationButtons,
OcelotInput,
},
created() {
this.icons = iconRegistry

View File

@ -143,13 +143,15 @@ exports[`invites.vue renders 1`] = `
style="display: none;"
>
</label>
<div
class="ds-input-wrap"
>
<!---->
<input
class="ds-input"
name="comment"
@ -157,8 +159,10 @@ exports[`invites.vue renders 1`] = `
tabindex="0"
type="text"
/>
<!---->
</div>
<transition-stub
name="ds-input-error"
>
@ -167,8 +171,8 @@ exports[`invites.vue renders 1`] = `
style="display: none;"
>
</div>
</transition-stub>
</div>

View File

@ -2,7 +2,7 @@
<form class="settings-form" @submit.prevent="onSubmit" novalidate>
<os-card>
<h2 class="title">{{ $t('settings.data.name') }}</h2>
<ds-input
<ocelot-input
id="name"
model="name"
icon="user"
@ -13,14 +13,14 @@
"
:placeholder="$t('settings.data.namePlaceholder')"
/>
<ds-input id="slug" model="slug" icon="at" :label="$t('settings.data.labelSlug')" />
<ocelot-input id="slug" model="slug" icon="at" :label="$t('settings.data.labelSlug')" />
<location-select
class="location-selet"
v-model="formData.locationName"
:canBeCleared="!$env.REQUIRE_LOCATION"
/>
<!-- eslint-enable vue/use-v-on-exact -->
<ds-input
<ocelot-input
id="about"
model="about"
type="textarea"
@ -48,6 +48,7 @@ import { iconRegistry } from '~/utils/iconRegistry'
import { mapGetters, mapMutations } from 'vuex'
import UniqueSlugForm from '~/components/utils/UniqueSlugForm'
import LocationSelect from '~/components/Select/LocationSelect'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
import { updateUserMutation } from '~/graphql/User'
import scrollToContent from './scroll-to-content.js'
import formValidation from '~/mixins/formValidation'
@ -60,6 +61,7 @@ export default {
OsCard,
OsIcon,
LocationSelect,
OcelotInput,
},
data() {
return {

View File

@ -2,14 +2,14 @@
<form @submit.prevent="onSubmit" novalidate>
<os-card>
<h2 class="title">{{ $t('settings.email.name') }}</h2>
<ds-input
<ocelot-input
id="email"
model="email"
icon="envelope"
disabled
:label="$t('settings.email.labelNewEmail')"
/>
<ds-input
<ocelot-input
id="nonce"
model="nonce"
icon="question-circle"
@ -27,10 +27,11 @@
import { OsButton, OsCard, OsIcon } from '@ocelot-social/ui'
import { iconRegistry } from '~/utils/iconRegistry'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
mixins: [formValidation],
components: { OsButton, OsCard, OsIcon },
components: { OsButton, OsCard, OsIcon, OcelotInput },
data() {
return {
formData: {

View File

@ -8,7 +8,13 @@
<form v-else @submit.prevent="onSubmit" novalidate>
<os-card>
<h2 class="title">{{ $t('settings.email.name') }}</h2>
<ds-input id="email" model="email" icon="envelope" :label="$t('settings.email.labelEmail')" />
<ocelot-input
id="email"
model="email"
type="email"
icon="envelope"
:label="$t('settings.email.labelEmail')"
/>
<div class="ds-mb-large backendErrors" v-if="backendErrors">
<p class="ds-text ds-text-center ds-text-bold ds-text-danger">
{{ backendErrors.message }}
@ -36,6 +42,7 @@ import { AddEmailAddressMutation } from '~/graphql/EmailAddress.js'
import { SweetalertIcon } from 'vue-sweetalert-icons'
import scrollToContent from '../scroll-to-content.js'
import formValidation from '~/mixins/formValidation'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
mixins: [scrollToContent, formValidation],
@ -44,6 +51,7 @@ export default {
OsCard,
OsIcon,
SweetalertIcon,
OcelotInput,
},
created() {
this.icons = iconRegistry

View File

@ -14,7 +14,7 @@
<social-media-list-item :item="item" />
</template>
<template #edit-item>
<ds-input
<ocelot-input
id="editSocialMedia"
model="socialMediaUrl"
type="text"
@ -38,6 +38,7 @@ import {
import MySomethingList from '~/components/_new/features/MySomethingList/MySomethingList.vue'
import SocialMediaListItem from '~/components/_new/features/SocialMedia/SocialMediaListItem.vue'
import scrollToContent from './scroll-to-content.js'
import OcelotInput from '~/components/OcelotInput/OcelotInput.vue'
export default {
mixins: [scrollToContent],
@ -45,6 +46,7 @@ export default {
OsCard,
MySomethingList,
SocialMediaListItem,
OcelotInput,
},
data() {
return {

View File

@ -7,7 +7,6 @@ import Filters from '~/plugins/vue-filters'
import InfiniteLoading from '~/plugins/vue-infinite-loading'
import Directives from '~/plugins/vue-directives'
import VueObserveVisibility from '~/plugins/vue-observe-visibility'
require('intersection-observer')
// Fail tests on Vue warnings