Skip to content

Commit

Permalink
Replace Wikit Textarea with Codex Textarea component (#771)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
guergana authored and chukarave committed Dec 19, 2023
1 parent 0ad01ff commit 87c8b24
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 191 deletions.
1 change: 1 addition & 0 deletions public/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"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.",
"find-more": "Find out more",
"item-form-title": "Which Items should be checked?",
"item-form-progress-bar-aria-label": "Progress Bar indicating loading state when submittion Check Items",
"item-form-id-input-label": "Please add one Item identifier per line",
"item-form-id-input-placeholder": "For example:\nQ80378\nQ33602\nQ1459\nQ4524",
"item-form-submit": "Check Items",
Expand Down
1 change: 1 addition & 0 deletions public/i18n/qqq.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"about-mismatch-finder-description": "A short text to describe the Mismatch Finder tool",
"find-more": "A call to action for more information",
"item-form-title": "The title of the form that filters Mismatches by Item id",
"item-form-progress-bar-aria-label": "The aria label for screen reader for the Progress Bar indicating loading state when submittion Check Items",
"item-form-id-input-label": "The label for the Item id input",
"item-form-id-input-placeholder": "The placeholder for the Item id input",
"item-form-submit": "The call to action on the Item id form's submit button",
Expand Down
112 changes: 112 additions & 0 deletions resources/js/Components/ItemIdSearchTextarea.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<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 setup lang="ts">
import { ref, inject } from 'vue';
import { Ref } from 'vue';
import { useI18n } from 'vue-banana-i18n';
import { useStore } from '../store';
import { CdxTextArea, CdxField, CdxProgressBar } from "@wikimedia/codex";
import ValidationError from '../types/ValidationError';
// Run it with compat mode
// https://v3-migration.vuejs.org/breaking-changes/v-model.html
CdxTextArea.compatConfig = {
...CdxTextArea.compatConfig,
COMPONENT_V_MODEL: false,
};
const validationError: Ref<ValidationError> = ref(null);
const messages = useI18n();
const store = useStore();
const textareaInputValue = ref(store.lastSearchedIds);
const MAX_NUM_IDS = inject('MAX_NUM_IDS');
defineProps<{loading: boolean}>();
function splitInput(): Array<string> {
return textareaInputValue.value.split( '\n' );
}
function sanitizeArray(): Array<string> {
// this filter function removes all falsy values
// see: https://stackoverflow.com/a/281335/1619792
return splitInput().filter(x => x);
}
function serializeInput(): string {
return sanitizeArray().join('|');
}
function validate(): void {
validationError.value = null;
const typeError = 'error';
const rules = [{
check: (ids: Array<string>) => ids.length < 1,
type: typeError,
message: { [typeError]: messages.i18n('item-form-error-message-empty') as string }
},
{
check: (ids: Array<string>) => ids.length > (MAX_NUM_IDS as number),
type: typeError,
message: { [typeError]: messages.i18n('item-form-error-message-max', MAX_NUM_IDS) as string }
},
{
check: (ids: Array<string>) => !ids.every(value => /^[Qq]\d+$/.test( value.trim() )),
type: typeError,
message: { [typeError]: messages.i18n('item-form-error-message-invalid') as string }
}];
const sanitized = sanitizeArray();
for(const {check, type, message} of rules){
if(check(sanitized)){
validationError.value = { type, message };
return;
}
}
}
defineExpose({validate, serializeInput, validationError});
</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>
104 changes: 36 additions & 68 deletions resources/js/Pages/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
<item-id-search-textarea
:loading="loading"
:error="validationError"
v-model="form.itemsInput"
ref="textarea"
/>
<div class="form-buttons">
<cdx-button
Expand All @@ -106,20 +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 ItemIdSearchTextarea from '../Components/ItemIdSearchTextarea.vue';
import { cdxIconDie, cdxIconInfo } from '@wikimedia/codex-icons';
import { defineComponent } from 'vue';
import { defineComponent, ref } from 'vue';
import ValidationError from '../types/ValidationError';
interface HomeState {
form: {
itemsInput: string
},
validationError: null|{
type: string,
message: string
},
faqDialog: boolean
validationError: null|ValidationError,
faqDialog: boolean
}
interface ErrorMessages {
Expand All @@ -130,72 +121,37 @@
errors : { [ key : string ] : string }
}
export const MAX_NUM_IDS = 600;
export default defineComponent({
components: {
CdxDialog,
CdxButton,
CdxIcon,
CdxMessage,
InertiaHead,
TextArea,
ItemIdSearchTextarea,
InertiaHead
},
setup() {
const store = useStore();
const textareaInputValue = ref(store.lastSearchedIds);
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 ItemIdSearchTextarea>).validate();
if(this.validationError) {
if((this.$refs.textarea as InstanceType<typeof ItemIdSearchTextarea>).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 ItemIdSearchTextarea>).serializeInput() }
);
},
showRandom(): void {
this.$inertia.get( '/random' );
Expand All @@ -216,11 +172,7 @@
...mapState(useStore, ['loading']),
},
data(): HomeState {
const store = useStore();
return {
form: {
itemsInput: store.lastSearchedIds
},
validationError: null,
faqDialog: false
}
Expand Down Expand Up @@ -272,5 +224,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>
5 changes: 3 additions & 2 deletions resources/js/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import './bootstrap';
import {createApp, h} from 'vue';
import {createPinia} from 'pinia';
import {createInertiaApp} from '@inertiajs/inertia-vue3';
import getI18nMessages, { I18nMessages } from './lib/i18n';
import {createI18n} from 'vue-banana-i18n'
import getI18nMessages from './lib/i18n';
import {createI18n} from 'vue-banana-i18n';
import bubble from './lib/bubble';
import Error from './Pages/Error.vue';
import Layout from './Pages/Layout.vue';
Expand Down Expand Up @@ -36,6 +36,7 @@ import Layout from './Pages/Layout.vue';
.use(i18nPlugin)
.use(pinia)
.use(plugin)
.provide('MAX_NUM_IDS', 600)
.mount(el)
}
});
Expand Down
6 changes: 6 additions & 0 deletions resources/js/types/ValidationError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type ValidationError = {
type: string,
message: { [key : string] : string }
}

export default ValidationError;
4 changes: 2 additions & 2 deletions tests/Browser/ItemsFormTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ public function test_can_submit_list_of_item_ids()
});
}

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

$this->assertStringContainsString(
'--warning',
'--error',
$browser->attribute('@items-input-validation-message', 'class')
);
});
Expand Down
2 changes: 1 addition & 1 deletion tests/Browser/Pages/HomePage.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function elements()
return [
'@form' => '#items-form',
'@items-input' => '@form textarea',
'@items-input-validation-message' => '@form .wikit-TextArea .wikit-ValidationMessage'
'@items-input-validation-message' => '@form .cdx-field__validation-message .cdx-message'
];
}
}
Loading

0 comments on commit 87c8b24

Please sign in to comment.