diff --git a/jest.config.js b/jest.config.js index 3a09486e7..dd769699d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,7 +12,8 @@ module.exports = { "transform": { ".*\\.(vue)$": "/node_modules/@vue/vue3-jest", "^.+\\.js$": "/node_modules/babel-jest", - "^.+\\.tsx?$": "/node_modules/ts-jest" + "^.+\\.tsx?$": "/node_modules/ts-jest", + '.+\\.svg$': '/tests/Vue/helpers/jest-svg-component-transformer.js' }, // (Optional) This file helps you later for global settings "setupFilesAfterEnv": [ diff --git a/package-lock.json b/package-lock.json index 5a6da58aa..c8187177f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "@inertiajs/inertia-vue3": "^0.6.0", "date-fns": "^3.3.1", "lodash": "^4.17.21", + "lodash.debounce": "^4.0.8", "pinia": "^2.1.7", "ress": "^5.0.2", "vue": "^3.3.8", @@ -11785,8 +11786,7 @@ "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "node_modules/lodash.isequal": { "version": "4.5.0", diff --git a/package.json b/package.json index 35bc33693..03694f252 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@inertiajs/inertia-vue3": "^0.6.0", "date-fns": "^3.3.1", "lodash": "^4.17.21", + "lodash.debounce": "^4.0.8", "pinia": "^2.1.7", "ress": "^5.0.2", "vue": "^3.3.8", diff --git a/resources/js/Components/LanguageSelector.vue b/resources/js/Components/LanguageSelector.vue index 67cebbbf3..620f26b26 100644 --- a/resources/js/Components/LanguageSelector.vue +++ b/resources/js/Components/LanguageSelector.vue @@ -39,14 +39,16 @@ import LanguageSelectorOptionsMenu from "./LanguageSelectorOptionsMenu.vue"; import LanguageSelectorInput from "./LanguageSelectorInput.vue"; import Language from '../types/Language'; import closeUrlSvg from '../../img/close.svg'; - +import axios from 'axios'; import {ref, computed} from "vue"; import type {Ref} from 'vue'; import languageData from "@wikimedia/language-data"; +import debounce from "lodash.debounce"; const searchInput: Ref = ref(''); const highlightedIndex: Ref = ref(-1); const closeUrl = ref(closeUrlSvg); +const apiLanguageCodes = ref(['']); const input = ref | null>(null); @@ -63,17 +65,35 @@ const languages = computed(() => { }); const shownLanguages = computed(() => { - return languages.value.filter((language) => - language.code.startsWith(searchInput.value.toLowerCase()) || - language.autonym.toLowerCase().includes(searchInput.value.toLowerCase()), - ); + return languages.value.filter((language) => + language.code.startsWith(searchInput.value.toLowerCase()) || + language.autonym.toLowerCase().includes(searchInput.value.toLowerCase()) || + apiLanguageCodes.value.includes(language.code) + ) }); function onInput(searchedLanguage: string): void { searchInput.value = searchedLanguage; + if (searchInput.value) { + debouncedApiLanguageSearch(searchInput.value); + } + highlightedIndex.value = 0; } +const debouncedApiLanguageSearch = debounce(async (debouncedInputValue: string) => { + await axios.get( + 'https://www.wikidata.org/w/api.php?action=languagesearch&format=json&formatversion=2', + { + params: { + search: debouncedInputValue, + origin: '*' // avoid CORS console errors + } + }).then((response) => { + apiLanguageCodes.value = Object.keys(response.data.languagesearch); + }); +}, 200); + function onSelect(languageCode: string): void { emit('select', languageCode); } diff --git a/tests/Vue/Components/LanguageSelector.spec.js b/tests/Vue/Components/LanguageSelector.spec.js new file mode 100644 index 000000000..4c26158a8 --- /dev/null +++ b/tests/Vue/Components/LanguageSelector.spec.js @@ -0,0 +1,44 @@ +import { mount } from '@vue/test-utils'; +import LanguageSelector from '@/Components/LanguageSelector.vue'; +import { createI18n } from 'vue-banana-i18n'; + +const i18n = createI18n({ + messages: {}, + locale: 'en', + wikilinks: true +}); + +describe('LanguageSelector.vue', () => { + it('suggests the relevant language upon input', async () => { + const wrapper = mount(LanguageSelector, { + global: { + plugins: [i18n], + }}); + + const input = wrapper.find('input'); + + expect(input.exists()).toBe(true); + + await input.setValue('deu'); + const listItems = await wrapper.findAll('.languageSelector__options-menu__languages-list__item'); + + expect(listItems.at(0).text()).toContain('Deutsch'); + }); + + it('suggests the relevant language upon RTL input', async () => { + const wrapper = mount(LanguageSelector, { + global: { + plugins: [i18n], + }}); + + const input = wrapper.find('input'); + + expect(input.exists()).toBe(true); + + await input.setValue('עב'); + const listItems = await wrapper.findAll('.languageSelector__options-menu__languages-list__item'); + + expect(listItems.at(0).text()).toContain('עברית'); + }); + +}); diff --git a/tests/Vue/helpers/jest-svg-component-transformer.js b/tests/Vue/helpers/jest-svg-component-transformer.js new file mode 100644 index 000000000..4d9d5bd12 --- /dev/null +++ b/tests/Vue/helpers/jest-svg-component-transformer.js @@ -0,0 +1,12 @@ +module.exports = { + process(sourceText, sourcePath) { + const mockComponent = { + name: sourcePath, + template: sourceText + } + + return { + code: `module.exports = ${JSON.stringify(mockComponent)};` + } + } + }