-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace Wikit Textarea with Codex Textarea component #771
Changes from 11 commits
4dd4db7
e4e5ff7
0410cb1
b62c91c
9fd7600
22c83c6
9d70229
9aa3703
5021f51
242bf73
6028a8f
64e9048
54cd3f7
97f4f8e
fcf168c
5dffb0c
94b4505
fdb98aa
ea871ca
d50dd53
011a249
35af9ff
e6ea197
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
<template> | ||
<cdx-field | ||
:status="validationError ? validationError.type : 'default'" | ||
:messages="validationError ? validationError.message : null" | ||
> | ||
<div class="progress-bar-wrapper"> | ||
<cdx-progress-bar v-if="loading" :aria-label="$i18n('item-form-progress-bar-aria-label')" /> | ||
</div> | ||
<cdx-text-area | ||
:label="$i18n('item-form-id-input-label')" | ||
:placeholder="$i18n('item-form-id-input-placeholder')" | ||
:rows="8" | ||
:status="validationError ? validationError.type : 'default'" | ||
v-model="textareaInputValue" | ||
/> | ||
</cdx-field> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent, ref } from 'vue'; | ||
import { useStore } from '../store'; | ||
import { MAX_NUM_IDS } from '../Pages/Home.vue'; | ||
guergana marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import { CdxTextArea, CdxField, CdxProgressBar } from "@wikimedia/codex"; | ||
|
||
// Run it with compat mode | ||
// https://v3-migration.vuejs.org/breaking-changes/v-model.html | ||
CdxTextArea.compatConfig = { | ||
guergana marked this conversation as resolved.
Show resolved
Hide resolved
|
||
...CdxTextArea.compatConfig, | ||
COMPONENT_V_MODEL: false, | ||
}; | ||
|
||
export default defineComponent({ | ||
components: { | ||
CdxField, | ||
CdxProgressBar, | ||
CdxTextArea, | ||
}, | ||
setup() { | ||
const store = useStore(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we even need to rely on the state store here, if this is to be rally made reusable, we would get the IDs from a prop. |
||
const textareaInputValue = ref(store.lastSearchedIds); | ||
|
||
return { | ||
textareaInputValue | ||
}; | ||
}, | ||
props: { | ||
loading: { | ||
type: Boolean, | ||
default: false | ||
} | ||
}, | ||
methods: { | ||
splitInput: function(): Array<string> { | ||
return this.textareaInputValue.split( '\n' ); | ||
}, | ||
sanitizeArray: function(): Array<string> { | ||
// this filter function removes all falsy values | ||
// see: https://stackoverflow.com/a/281335/1619792 | ||
return this.splitInput().filter(x => x); | ||
}, | ||
serializeInput: function(): string { | ||
return this.sanitizeArray().join('|'); | ||
}, | ||
validate(): void { | ||
this.validationError = null; | ||
|
||
const typeError = 'error'; | ||
|
||
const rules = [{ | ||
check: (ids: Array<string>) => ids.length < 1, | ||
type: typeError, | ||
message: { [typeError]: this.$i18n('item-form-error-message-empty') } | ||
}, | ||
{ | ||
check: (ids: Array<string>) => ids.length > MAX_NUM_IDS, | ||
type: 'error', | ||
message: { [typeError]: this.$i18n('item-form-error-message-max', MAX_NUM_IDS) } | ||
}, | ||
{ | ||
check: (ids: Array<string>) => !ids.every(value => /^[Qq]\d+$/.test( value.trim() )), | ||
type: 'error', | ||
message: { [typeError]: this.$i18n('item-form-error-message-invalid') } | ||
}]; | ||
|
||
const sanitized = this.sanitizeArray(); | ||
|
||
for(const {check, type, message} of rules){ | ||
if(check(sanitized)){ | ||
this.validationError = { type, message }; | ||
return; | ||
} | ||
} | ||
}, | ||
}, | ||
data() { | ||
return { | ||
validationError: null | ||
} | ||
}, | ||
}); | ||
</script> | ||
|
||
<style lang="scss"> | ||
|
||
.cdx-field__control { | ||
position: relative; | ||
width: 100%; | ||
|
||
.progress-bar-wrapper { | ||
position: absolute; | ||
top: 50%; | ||
width: 100%; | ||
|
||
.cdx-progress-bar { | ||
width: 50%; | ||
margin: auto; | ||
} | ||
} | ||
} | ||
|
||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,13 +78,9 @@ | |
</cdx-button> | ||
</div> | ||
<form id="items-form" @submit.prevent="send"> | ||
<text-area | ||
:label="$i18n('item-form-id-input-label')" | ||
:placeholder="$i18n('item-form-id-input-placeholder')" | ||
:rows="8" | ||
<textarea-home | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not so sure about calling the component
Since the component is in charge of providing a search field for Item IDs, I think a better name for it would be something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, wasn't sure what name to give it. Suggestions always come up faster in the review :D Thanks for the name. I still want to make sure it is clear that this is the textarea. I will call it |
||
:loading="loading" | ||
:error="validationError" | ||
v-model="form.itemsInput" | ||
ref="textarea" | ||
/> | ||
<div class="form-buttons"> | ||
<cdx-button | ||
|
@@ -106,18 +102,15 @@ | |
import { Head as InertiaHead } from '@inertiajs/inertia-vue3'; | ||
import { mapState } from 'pinia'; | ||
import { useStore } from '../store'; | ||
import { TextArea } from '@wmde/wikit-vue-components'; | ||
import { CdxDialog, CdxButton, CdxIcon, CdxMessage } from "@wikimedia/codex"; | ||
import TextareaHome from '../Components/TextareaHome.vue'; | ||
import { cdxIconDie, cdxIconInfo } from '@wikimedia/codex-icons'; | ||
import { defineComponent } from 'vue'; | ||
import { defineComponent, ref } from 'vue'; | ||
|
||
interface HomeState { | ||
form: { | ||
itemsInput: string | ||
}, | ||
validationError: null|{ | ||
type: string, | ||
message: string | ||
message: object | ||
}, | ||
faqDialog: boolean | ||
} | ||
|
@@ -138,64 +131,31 @@ | |
CdxButton, | ||
CdxIcon, | ||
CdxMessage, | ||
InertiaHead, | ||
TextArea, | ||
TextareaHome, | ||
InertiaHead | ||
}, | ||
setup() { | ||
const store = useStore(); | ||
const textareaInputValue = ref(store.lastSearchedIds); | ||
guergana marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return { | ||
cdxIconDie, | ||
cdxIconInfo | ||
cdxIconInfo, | ||
textareaInputValue | ||
}; | ||
}, | ||
methods: { | ||
splitInput: function(): Array<string> { | ||
return this.form.itemsInput.split( '\n' ); | ||
}, | ||
sanitizeArray: function(): Array<string> { | ||
// this filter function removes all falsy values | ||
// see: https://stackoverflow.com/a/281335/1619792 | ||
return this.splitInput().filter(x => x); | ||
}, | ||
serializeInput: function(): string { | ||
return this.sanitizeArray().join('|'); | ||
}, | ||
validate(): void { | ||
this.validationError = null; | ||
|
||
const rules = [{ | ||
check: (ids: Array<string>) => ids.length < 1, | ||
type: 'warning', | ||
message: this.$i18n('item-form-error-message-empty') | ||
}, | ||
{ | ||
check: (ids: Array<string>) => ids.length > MAX_NUM_IDS, | ||
type: 'error', | ||
message: this.$i18n('item-form-error-message-max', MAX_NUM_IDS) | ||
}, | ||
{ | ||
check: (ids: Array<string>) => !ids.every(value => /^[Qq]\d+$/.test( value.trim() )), | ||
type: 'error', | ||
message: this.$i18n('item-form-error-message-invalid') | ||
}]; | ||
|
||
const sanitized = this.sanitizeArray(); | ||
|
||
for(const {check, type, message} of rules){ | ||
if(check(sanitized)){ | ||
this.validationError = { type, message }; | ||
return; | ||
} | ||
} | ||
}, | ||
send(): void { | ||
this.validate(); | ||
(this.$refs.textarea as InstanceType<typeof TextareaHome>).validate(); | ||
|
||
if(this.validationError) { | ||
if((this.$refs.textarea as InstanceType<typeof TextareaHome>).validationError) { | ||
return; | ||
} | ||
const store = useStore(); | ||
store.saveSearchedIds( this.form.itemsInput ); | ||
this.$inertia.get( '/results', { ids: this.serializeInput() } ); | ||
store.saveSearchedIds( this.textareaInputValue ); | ||
this.$inertia.get( '/results', | ||
{ ids: (this.$refs.textarea as InstanceType<typeof TextareaHome>).serializeInput() } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer it if we didn't directly call component methods. While this can be done with composition API using |
||
); | ||
}, | ||
showRandom(): void { | ||
this.$inertia.get( '/random' ); | ||
|
@@ -216,11 +176,7 @@ | |
...mapState(useStore, ['loading']), | ||
}, | ||
data(): HomeState { | ||
const store = useStore(); | ||
return { | ||
form: { | ||
itemsInput: store.lastSearchedIds | ||
}, | ||
validationError: null, | ||
faqDialog: false | ||
} | ||
|
@@ -272,5 +228,21 @@ | |
.form-buttons { | ||
text-align: end; | ||
} | ||
|
||
.cdx-field__control { | ||
position: relative; | ||
width: 100%; | ||
|
||
.progress-bar-wrapper { | ||
position: absolute; | ||
top: 50%; | ||
width: 100%; | ||
|
||
.cdx-progress-bar { | ||
width: 50%; | ||
margin: auto; | ||
} | ||
} | ||
} | ||
} | ||
</style> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a new component, I think we should use the composition API instead of the options API already. That is, we can use
<script setup lang="ts">
here and not usedefineComponent
. See: