Skip to content

Commit 87c8b24

Browse files
guerganachukarave
authored andcommitted
Replace Wikit Textarea with Codex Textarea component (#771)
* Replace Wikit Textarea with Codex Textarea component Bug: T347190 * Fix v-model and add functionality * Remove this.form.itemsInput in favor of new v-model syntax * Fix errors * Remove trailing comma * Update browser tests * Update Home.vue * Update Home.spec.js * Put textarea in a wrapper component * Adjust tests to new TextareaHome component * Fix linting * Move MAX_NUM_IDS to TextArea component * Fix indentation * Rename component to ItemIdSearchTextarea * [WiP] rewrite component using Composition API * Commit unsuccesful testing export of i18n :( * Adjust i18n plugin to composition API with useI18n * Refactor global variable MAX_NUM_IDS to vue3 format * Fix typescript errors * Update HomeState to use the new ValidationError type * Add spaces between methods * Change name of internationalizaton plugin variable * Remove unneeded options in createI18n plugin
1 parent 0ad01ff commit 87c8b24

File tree

10 files changed

+309
-191
lines changed

10 files changed

+309
-191
lines changed

public/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"about-mismatch-finder-description": "The Mismatch Finder shows you data in Wikidata that differs from the data in another database, catalog or website (for example, someone's date of birth in Wikidata doesn't match the corresponding entry in the German National Library's catalog). Mismatches like this need fixing, and the Mismatch Finder helps you to do just that.",
1212
"find-more": "Find out more",
1313
"item-form-title": "Which Items should be checked?",
14+
"item-form-progress-bar-aria-label": "Progress Bar indicating loading state when submittion Check Items",
1415
"item-form-id-input-label": "Please add one Item identifier per line",
1516
"item-form-id-input-placeholder": "For example:\nQ80378\nQ33602\nQ1459\nQ4524",
1617
"item-form-submit": "Check Items",

public/i18n/qqq.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"about-mismatch-finder-description": "A short text to describe the Mismatch Finder tool",
1010
"find-more": "A call to action for more information",
1111
"item-form-title": "The title of the form that filters Mismatches by Item id",
12+
"item-form-progress-bar-aria-label": "The aria label for screen reader for the Progress Bar indicating loading state when submittion Check Items",
1213
"item-form-id-input-label": "The label for the Item id input",
1314
"item-form-id-input-placeholder": "The placeholder for the Item id input",
1415
"item-form-submit": "The call to action on the Item id form's submit button",
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<template>
2+
<cdx-field
3+
:status="validationError ? validationError.type : 'default'"
4+
:messages="validationError ? validationError.message : null"
5+
>
6+
<div class="progress-bar-wrapper">
7+
<cdx-progress-bar v-if="loading" :aria-label="$i18n('item-form-progress-bar-aria-label')" />
8+
</div>
9+
<cdx-text-area
10+
:label="$i18n('item-form-id-input-label')"
11+
:placeholder="$i18n('item-form-id-input-placeholder')"
12+
:rows="8"
13+
:status="validationError ? validationError.type : 'default'"
14+
v-model="textareaInputValue"
15+
/>
16+
</cdx-field>
17+
</template>
18+
19+
<script setup lang="ts">
20+
import { ref, inject } from 'vue';
21+
import { Ref } from 'vue';
22+
import { useI18n } from 'vue-banana-i18n';
23+
import { useStore } from '../store';
24+
import { CdxTextArea, CdxField, CdxProgressBar } from "@wikimedia/codex";
25+
import ValidationError from '../types/ValidationError';
26+
27+
// Run it with compat mode
28+
// https://v3-migration.vuejs.org/breaking-changes/v-model.html
29+
CdxTextArea.compatConfig = {
30+
...CdxTextArea.compatConfig,
31+
COMPONENT_V_MODEL: false,
32+
};
33+
34+
const validationError: Ref<ValidationError> = ref(null);
35+
36+
const messages = useI18n();
37+
38+
const store = useStore();
39+
const textareaInputValue = ref(store.lastSearchedIds);
40+
41+
const MAX_NUM_IDS = inject('MAX_NUM_IDS');
42+
43+
defineProps<{loading: boolean}>();
44+
45+
function splitInput(): Array<string> {
46+
return textareaInputValue.value.split( '\n' );
47+
}
48+
49+
function sanitizeArray(): Array<string> {
50+
// this filter function removes all falsy values
51+
// see: https://stackoverflow.com/a/281335/1619792
52+
return splitInput().filter(x => x);
53+
}
54+
55+
function serializeInput(): string {
56+
return sanitizeArray().join('|');
57+
}
58+
59+
function validate(): void {
60+
validationError.value = null;
61+
62+
const typeError = 'error';
63+
64+
const rules = [{
65+
check: (ids: Array<string>) => ids.length < 1,
66+
type: typeError,
67+
message: { [typeError]: messages.i18n('item-form-error-message-empty') as string }
68+
},
69+
{
70+
check: (ids: Array<string>) => ids.length > (MAX_NUM_IDS as number),
71+
type: typeError,
72+
message: { [typeError]: messages.i18n('item-form-error-message-max', MAX_NUM_IDS) as string }
73+
},
74+
{
75+
check: (ids: Array<string>) => !ids.every(value => /^[Qq]\d+$/.test( value.trim() )),
76+
type: typeError,
77+
message: { [typeError]: messages.i18n('item-form-error-message-invalid') as string }
78+
}];
79+
80+
const sanitized = sanitizeArray();
81+
82+
for(const {check, type, message} of rules){
83+
if(check(sanitized)){
84+
validationError.value = { type, message };
85+
return;
86+
}
87+
}
88+
}
89+
90+
defineExpose({validate, serializeInput, validationError});
91+
92+
</script>
93+
94+
<style lang="scss">
95+
96+
.cdx-field__control {
97+
position: relative;
98+
width: 100%;
99+
100+
.progress-bar-wrapper {
101+
position: absolute;
102+
top: 50%;
103+
width: 100%;
104+
105+
.cdx-progress-bar {
106+
width: 50%;
107+
margin: auto;
108+
}
109+
}
110+
}
111+
112+
</style>

resources/js/Pages/Home.vue

Lines changed: 36 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,9 @@
7878
</cdx-button>
7979
</div>
8080
<form id="items-form" @submit.prevent="send">
81-
<text-area
82-
:label="$i18n('item-form-id-input-label')"
83-
:placeholder="$i18n('item-form-id-input-placeholder')"
84-
:rows="8"
81+
<item-id-search-textarea
8582
:loading="loading"
86-
:error="validationError"
87-
v-model="form.itemsInput"
83+
ref="textarea"
8884
/>
8985
<div class="form-buttons">
9086
<cdx-button
@@ -106,20 +102,15 @@
106102
import { Head as InertiaHead } from '@inertiajs/inertia-vue3';
107103
import { mapState } from 'pinia';
108104
import { useStore } from '../store';
109-
import { TextArea } from '@wmde/wikit-vue-components';
110105
import { CdxDialog, CdxButton, CdxIcon, CdxMessage } from "@wikimedia/codex";
106+
import ItemIdSearchTextarea from '../Components/ItemIdSearchTextarea.vue';
111107
import { cdxIconDie, cdxIconInfo } from '@wikimedia/codex-icons';
112-
import { defineComponent } from 'vue';
108+
import { defineComponent, ref } from 'vue';
109+
import ValidationError from '../types/ValidationError';
113110
114111
interface HomeState {
115-
form: {
116-
itemsInput: string
117-
},
118-
validationError: null|{
119-
type: string,
120-
message: string
121-
},
122-
faqDialog: boolean
112+
validationError: null|ValidationError,
113+
faqDialog: boolean
123114
}
124115
125116
interface ErrorMessages {
@@ -130,72 +121,37 @@
130121
errors : { [ key : string ] : string }
131122
}
132123
133-
export const MAX_NUM_IDS = 600;
134-
135124
export default defineComponent({
136125
components: {
137126
CdxDialog,
138127
CdxButton,
139128
CdxIcon,
140129
CdxMessage,
141-
InertiaHead,
142-
TextArea,
130+
ItemIdSearchTextarea,
131+
InertiaHead
143132
},
144133
setup() {
134+
const store = useStore();
135+
const textareaInputValue = ref(store.lastSearchedIds);
136+
145137
return {
146138
cdxIconDie,
147-
cdxIconInfo
139+
cdxIconInfo,
140+
textareaInputValue
148141
};
149142
},
150143
methods: {
151-
splitInput: function(): Array<string> {
152-
return this.form.itemsInput.split( '\n' );
153-
},
154-
sanitizeArray: function(): Array<string> {
155-
// this filter function removes all falsy values
156-
// see: https://stackoverflow.com/a/281335/1619792
157-
return this.splitInput().filter(x => x);
158-
},
159-
serializeInput: function(): string {
160-
return this.sanitizeArray().join('|');
161-
},
162-
validate(): void {
163-
this.validationError = null;
164-
165-
const rules = [{
166-
check: (ids: Array<string>) => ids.length < 1,
167-
type: 'warning',
168-
message: this.$i18n('item-form-error-message-empty')
169-
},
170-
{
171-
check: (ids: Array<string>) => ids.length > MAX_NUM_IDS,
172-
type: 'error',
173-
message: this.$i18n('item-form-error-message-max', MAX_NUM_IDS)
174-
},
175-
{
176-
check: (ids: Array<string>) => !ids.every(value => /^[Qq]\d+$/.test( value.trim() )),
177-
type: 'error',
178-
message: this.$i18n('item-form-error-message-invalid')
179-
}];
180-
181-
const sanitized = this.sanitizeArray();
182-
183-
for(const {check, type, message} of rules){
184-
if(check(sanitized)){
185-
this.validationError = { type, message };
186-
return;
187-
}
188-
}
189-
},
190144
send(): void {
191-
this.validate();
145+
(this.$refs.textarea as InstanceType<typeof ItemIdSearchTextarea>).validate();
192146
193-
if(this.validationError) {
147+
if((this.$refs.textarea as InstanceType<typeof ItemIdSearchTextarea>).validationError) {
194148
return;
195149
}
196150
const store = useStore();
197-
store.saveSearchedIds( this.form.itemsInput );
198-
this.$inertia.get( '/results', { ids: this.serializeInput() } );
151+
store.saveSearchedIds( this.textareaInputValue );
152+
this.$inertia.get( '/results',
153+
{ ids: (this.$refs.textarea as InstanceType<typeof ItemIdSearchTextarea>).serializeInput() }
154+
);
199155
},
200156
showRandom(): void {
201157
this.$inertia.get( '/random' );
@@ -216,11 +172,7 @@
216172
...mapState(useStore, ['loading']),
217173
},
218174
data(): HomeState {
219-
const store = useStore();
220175
return {
221-
form: {
222-
itemsInput: store.lastSearchedIds
223-
},
224176
validationError: null,
225177
faqDialog: false
226178
}
@@ -272,5 +224,21 @@
272224
.form-buttons {
273225
text-align: end;
274226
}
227+
228+
.cdx-field__control {
229+
position: relative;
230+
width: 100%;
231+
232+
.progress-bar-wrapper {
233+
position: absolute;
234+
top: 50%;
235+
width: 100%;
236+
237+
.cdx-progress-bar {
238+
width: 50%;
239+
margin: auto;
240+
}
241+
}
242+
}
275243
}
276244
</style>

resources/js/app.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import './bootstrap';
22
import {createApp, h} from 'vue';
33
import {createPinia} from 'pinia';
44
import {createInertiaApp} from '@inertiajs/inertia-vue3';
5-
import getI18nMessages, { I18nMessages } from './lib/i18n';
6-
import {createI18n} from 'vue-banana-i18n'
5+
import getI18nMessages from './lib/i18n';
6+
import {createI18n} from 'vue-banana-i18n';
77
import bubble from './lib/bubble';
88
import Error from './Pages/Error.vue';
99
import Layout from './Pages/Layout.vue';
@@ -36,6 +36,7 @@ import Layout from './Pages/Layout.vue';
3636
.use(i18nPlugin)
3737
.use(pinia)
3838
.use(plugin)
39+
.provide('MAX_NUM_IDS', 600)
3940
.mount(el)
4041
}
4142
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type ValidationError = {
2+
type: string,
3+
message: { [key : string] : string }
4+
}
5+
6+
export default ValidationError;

tests/Browser/ItemsFormTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ public function test_can_submit_list_of_item_ids()
4141
});
4242
}
4343

44-
public function test_empty_item_list_yields_warning()
44+
public function test_empty_item_list_yields_error()
4545
{
4646
$this->browse(function (Browser $browser) {
4747
$browser->visit(new HomePage)
4848
->press('.submit-ids')
4949
->assertSee('Please provide the Item identifiers that should be checked.');
5050

5151
$this->assertStringContainsString(
52-
'--warning',
52+
'--error',
5353
$browser->attribute('@items-input-validation-message', 'class')
5454
);
5555
});

tests/Browser/Pages/HomePage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function elements()
4141
return [
4242
'@form' => '#items-form',
4343
'@items-input' => '@form textarea',
44-
'@items-input-validation-message' => '@form .wikit-TextArea .wikit-ValidationMessage'
44+
'@items-input-validation-message' => '@form .cdx-field__validation-message .cdx-message'
4545
];
4646
}
4747
}

0 commit comments

Comments
 (0)