Skip to content

Commit

Permalink
Replace Wikit Lookup component with Codex
Browse files Browse the repository at this point in the history
Replace usages of Wikit's Lookup compontent in `ItemLookup` and
`SpellingVariantInput` with the `CdxLookup` component.

Bug: T370057
  • Loading branch information
codders committed Oct 7, 2024
1 parent a830f7b commit 4f68ec5
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 179 deletions.
20 changes: 13 additions & 7 deletions cypress/e2e/NewLexemeForm.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,16 @@ describe( 'NewLexemeForm', () => {
cy.get( '.wbl-snl-language-lookup input' )
.type( '=Q123', { delay: 0 } );
checkA11y( '.wbl-snl-language-lookup' );
cy.get( '.wbl-snl-language-lookup .wikit-OptionsMenu__item' ).click();
cy.get( 'li.cdx-menu-item' ).contains( 'No match was found' ).should( 'not.exist' );
checkA11y( '.wbl-snl-language-lookup' );
cy.get( '.wbl-snl-language-lookup .cdx-menu-item' ).click();

cy.wait( '@LanguageCodeRetrieval' );

cy.get( '.wbl-snl-lexical-category-lookup input' )
.type( '=Q456', { delay: 0 } );
cy.get( '.wbl-snl-lexical-category-lookup .wikit-OptionsMenu__item' ).click();
cy.get( 'li.cdx-menu-item' ).contains( 'No match was found' ).should( 'not.exist' );
cy.get( '.wbl-snl-lexical-category-lookup .cdx-menu-item' ).click();

cy.get( '.wbl-snl-form' )
.submit();
Expand All @@ -99,17 +102,20 @@ describe( 'NewLexemeForm', () => {

cy.get( '.wbl-snl-language-lookup input' )
.type( '=Q123', { delay: 0 } );
cy.get( '.wbl-snl-language-lookup .wikit-OptionsMenu__item' ).click();
cy.get( '.wbl-snl-language-lookup li.cdx-menu-item' ).contains( 'No match was found' ).should( 'not.exist' );
cy.get( '.wbl-snl-language-lookup .cdx-menu-item' ).click();

cy.get( '.wbl-snl-lexical-category-lookup input' )
.type( '=Q456', { delay: 0 } );
cy.get( '.wbl-snl-lexical-category-lookup .wikit-OptionsMenu__item' ).click();
cy.get( '.wbl-snl-lexical-category-lookup li.cdx-menu-item' ).contains( 'No match was found' ).should( 'not.exist' );
cy.get( '.wbl-snl-lexical-category-lookup .cdx-menu-item' ).click();

cy.wait( '@LanguageCodeRetrieval' );

cy.get( '.wbl-snl-spelling-variant-lookup input' )
.type( 'en-ca', { delay: 0 } );
cy.get( '.wbl-snl-spelling-variant-lookup .wikit-OptionsMenu__item' ).click();
cy.get( '.wbl-snl-spelling-variant-lookup li.cdx-menu-item' ).contains( 'No match was found' ).should( 'not.exist' );
cy.get( '.wbl-snl-spelling-variant-lookup .cdx-menu-item' ).click();

cy.get( '.wbl-snl-form' )
.submit();
Expand Down Expand Up @@ -151,12 +157,12 @@ describe( 'NewLexemeForm', () => {

cy.get( '.wbl-snl-language-lookup input' ).click();

cy.get( '.wbl-snl-language-lookup .wikit-OptionsMenu__item__label' )
cy.get( '.wbl-snl-language-lookup .cdx-menu-item__text__label' )
.then( ( $element ) => {
expect( $element ).to.have.text( 'test language' );
} );

cy.get( '.wbl-snl-language-lookup .wikit-OptionsMenu__item__description' )
cy.get( '.wbl-snl-language-lookup .cdx-menu-item__text__description' )
.then( ( $element ) => {
expect( $element ).to.have.text( 'test language description' );
} );
Expand Down
131 changes: 86 additions & 45 deletions src/components/ItemLookup.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
<script setup lang="ts">
import {
computed,
ComputedRef,
ref,
toRef,
} from 'vue';
import { SearchedItemOption } from '@/data-access/ItemSearcher';
import WikitLookup from './WikitLookup';
import {
CdxLookup,
CdxField,
MenuItemData,
ValidationStatusType,
ValidationMessages,
useModelWrapper,
} from '@wikimedia/codex';
import RequiredAsterisk from '@/components/RequiredAsterisk.vue';
import debounce from 'lodash/debounce';
import escapeRegExp from 'lodash/escapeRegExp';
import { useMessages } from '@/plugins/MessagesPlugin/Messages';
Expand All @@ -19,6 +29,7 @@ interface Props {
itemSuggestions?: SearchedItemOption[];
ariaRequired?: boolean;
}
const props = withDefaults( defineProps<Props>(), {
error: null,
itemSuggestions: () => [],
Expand All @@ -33,6 +44,8 @@ const emit = defineEmits( {
'update:searchInput': null,
} );
const selection = ref( null );
// itemSuggestions matching the current searchInput
const suggestedOptions = computed( () => {
// eslint-disable-next-line security/detect-non-literal-regexp -- escapeRegExp used
Expand Down Expand Up @@ -72,8 +85,7 @@ const onOptionSelected = ( value: SearchedItemOption | null ) => {
const debouncedSearchForItems = debounce( async ( debouncedInputValue: string ) => {
searchedOptions.value = await props.searchForItems( debouncedInputValue );
}, 150 );
const onSearchInput = ( inputValue: string ) => {
emit( 'update:searchInput', inputValue );
const onInput = ( inputValue: string ) => {
if ( inputValue.trim() === '' ) {
searchedOptions.value = [];
return;
Expand All @@ -88,71 +100,100 @@ const onSearchInput = ( inputValue: string ) => {
debouncedSearchForItems( inputValue );
};
const onScroll = async () => {
const onLoadMore = async () => {
const searchReults = await props.searchForItems(
props.searchInput,
searchedOptions.value.length,
);
searchedOptions.value = [ ...searchedOptions.value, ...searchReults ];
};
// the remaining setup translates multilingual SearchedItemOptions to monolingual WikitItemOptions
interface WikitMenuItem {
label: string;
description: string;
tag?: string;
}
interface WikitItemOption extends WikitMenuItem {
value: string;
}
function searchResultToMonolingualOption( searchResult: SearchedItemOption ): WikitItemOption {
// translate a single multilingual SearchedItemOption to a monolingual MenuItemData
function searchResultToMonolingualOption( searchResult: SearchedItemOption ): MenuItemData {
return {
label: searchResult.display.label?.value || searchResult.id,
description: searchResult.display.description?.value || '',
value: searchResult.id,
};
}
const wikitMenuItems = computed( () => {
// translate multilingual SearchedItemOptions to monolingual MenuItemData array
const codexMenuItems: ComputedRef<MenuItemData[]> = computed( () => {
return menuItems.value.map( searchResultToMonolingualOption );
} );
const wikitValue = computed( () => {
if ( props.value === null ) {
return null;
}
return searchResultToMonolingualOption( props.value );
} );
const onWikitOptionSelected = ( value: unknown ) => {
const wikitOption = value as WikitItemOption | null;
const searchOption = menuItems.value.find( ( item ) => item.id === wikitOption?.value );
const onCodexOptionSelected = ( selectedItem: string | null ) => {
const searchOption = menuItems.value.find( ( item ) => item.id === selectedItem );
return onOptionSelected( searchOption ?? null );
};
const messages = useMessages();
const menuConfig = {
visibleItemLimit: 6,
};
const fieldStatus = computed( (): ValidationStatusType => {
if ( !props.error ) {
return 'default';
}
return props.error.type;
} );
const errorMessages = computed( (): ValidationMessages => {
if ( props.error ) {
if ( props.error.type === 'error' ) {
return { error: props.error.message };
}
if ( props.error.type === 'warning' ) {
return { warning: props.error.message };
}
}
return {};
} );
/**
* We want to pass the searchInput property from the parent component
* to the child component. The searchInput property comes in read-only
* and receives updates from the parent (it is a ref / computed value).
* Use the `useModelWrapper` helper here to turn the read-only property
* into a computed value that emits updates on change.
*/
const searchInputWrapper = useModelWrapper(
toRef( props, 'searchInput' ),
emit,
'update:searchInput',
);
</script>

<template>
<wikit-lookup
:label="label"
:placeholder="placeholder"
:search-input="props.searchInput"
:menu-items="wikitMenuItems"
:value="wikitValue"
:error="error"
:aria-required="ariaRequired"
@update:search-input="onSearchInput"
@scroll="onScroll"
@input="onWikitOptionSelected"
>
<template #no-results>
{{ messages.getUnescaped( 'wikibase-entityselector-notfound' ) }}
<cdx-field
:status="fieldStatus"
:messages="errorMessages">
<cdx-lookup
v-model:selected="selection"
v-model:input-value="searchInputWrapper"
:aria-required="ariaRequired"
:placeholder="placeholder"
:menu-items="codexMenuItems"
:menu-config="menuConfig"
@load-more="onLoadMore"
@input="onInput"
@update:selected="onCodexOptionSelected"
>
<template #no-results>
{{ messages.getUnescaped( 'wikibase-entityselector-notfound' ) }}
</template>
</cdx-lookup>
<template #label>
{{ label }}<required-asterisk v-if="ariaRequired" />
</template>
<template #suffix>
<slot name="suffix" />
</template>
</wikit-lookup>
</cdx-field>
</template>

<style scoped lang="scss">
.wbl-snl-required-asterisk {
margin-inline-start: var( --dimension-spacing-xsmall );
}
</style>
22 changes: 18 additions & 4 deletions src/components/LanguageInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import { useMessages } from '@/plugins/MessagesPlugin/Messages';
import ItemLookup from '@/components/ItemLookup.vue';
import RequiredAsterisk from '@/components/RequiredAsterisk.vue';
import { useLanguageItemSearch } from '@/plugins/ItemSearchPlugin/LanguageItemSearch';
import { computed } from 'vue';
import { computed, toRef } from 'vue';
import { useConfig } from '@/plugins/ConfigPlugin/Config';
import { useModelWrapper } from '@wikimedia/codex';
interface Props {
modelValue: SearchedItemOption | null;
searchInput: string;
}
defineProps<Props>();
const props = defineProps<Props>();
defineEmits<{
const emit = defineEmits<{
( e: 'update:modelValue', modelValue: Props['modelValue'] ): void;
( e: 'update:searchInput', searchInput: Props['searchInput'] ): void;
}>();
Expand Down Expand Up @@ -45,6 +46,19 @@ const error = computed( () => {
};
} );
const config = useConfig();
/**
* We want to pass the searchInput property from the parent component
* to the child component. The searchInput property comes in read-only
* and receives updates from the parent (it is a ref / computed value).
* Use the `useModelWrapper` helper here to turn the read-only property
* into a computed value that emits updates on change.
*/
const searchInputWrapper = useModelWrapper(
toRef( props, 'searchInput' ),
emit,
'update:searchInput',
);
</script>

<script lang="ts">
Expand All @@ -58,13 +72,13 @@ export default {
<template>
<div class="wbl-snl-language-lookup">
<item-lookup
v-model:search-input="searchInputWrapper"
:label="messages.getUnescaped( 'wikibaselexeme-newlexeme-language' )"
:placeholder="messages.getUnescaped(
'wikibaselexeme-newlexeme-language-placeholder-with-example',
config.placeholderExampleData.languageLabel
)"
:value="modelValue"
:search-input="searchInput"
:search-for-items="searchForItems"
:error="error"
:aria-required="true"
Expand Down
2 changes: 1 addition & 1 deletion src/components/NewLexemeForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const spellingVariant = computed( {
get(): string {
return store.state.spellingVariant;
},
set( newSpellingVariant: string | null ): void {
set( newSpellingVariant: string | undefined ): void {
store.commit( SET_SPELLING_VARIANT, newSpellingVariant );
if ( newSpellingVariant ) {
store.commit( CLEAR_PER_FIELD_ERRORS, 'spellingVariantErrors' );
Expand Down
Loading

0 comments on commit 4f68ec5

Please sign in to comment.