Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Issue number: resolves #

---------

<!-- Please do not submit updates to dependencies unless it fixes an issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by this PR. -->

-
-
-

## Does this introduce a breaking change?

- [ ] Yes
- [ ] No

<!--
If this introduces a breaking change:
1. Describe the impact and migration path for existing applications below.
2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
342 changes: 342 additions & 0 deletions cypress/e2e/examples.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,348 @@ describe('Search descriptions with and without normalize', () => {
});
});

// // // // //
// // // // // Multi-language search normalize
// // // // //

describe('Multi-language search with searchNormalize: true', () => {
const id = 'multi-language-search-select';

it('go to section', () => {
cy.goToSection('Multi-language search normalize');
});

// Latin (French / Spanish)
it('Latin: finds Crème brûlée when searching "creme"', () => {
cy.open(id).search('creme').checkFirstOption('Crème brûlée');
});

it('Latin: finds Niño when searching "nino"', () => {
cy.getVs(id).search('nino').checkFirstOption('Niño');
});

// German
it('German: finds München when searching "Munchen"', () => {
cy.getVs(id).search('Munchen').checkFirstOption('München');
});

it('German: finds Mädchen when searching "Madchen"', () => {
cy.getVs(id).search('Madchen').checkFirstOption('Mädchen');
});

it('German: ß is atomic — searching "Grosse" does NOT find "Größe"', () => {
cy.getVs(id).search('Grosse').hasNoOptions();
});

// Norwegian
it('Norwegian: finds Ålesund when searching "Alesund" (å decomposes)', () => {
cy.getVs(id).search('Alesund').checkFirstOption('Ålesund');
});

it('Norwegian: ø is atomic — searching "Bjorn" does NOT find "Bjørn"', () => {
cy.getVs(id).search('Bjorn').hasNoOptions();
});

// Swedish
it('Swedish: finds Göteborg when searching "Goteborg"', () => {
cy.getVs(id).search('Goteborg').checkFirstOption('Göteborg');
});

it('Swedish: finds Malmö when searching "Malmo"', () => {
cy.getVs(id).search('Malmo').checkFirstOption('Malmö');
});

// Finnish
it('Finnish: finds Jyväskylä when searching "Jyvaskyla"', () => {
cy.getVs(id).search('Jyvaskyla').checkFirstOption('Jyväskylä');
});

it('Finnish: finds Hämeenlinna when searching "Hameenlinna"', () => {
cy.getVs(id).search('Hameenlinna').checkFirstOption('Hämeenlinna');
});

// Greek
it('Greek: finds Ένα when searching with accent', () => {
cy.getVs(id).search('Ένα').checkFirstOption('Ένα');
});

it('Greek: finds Ένα when searching "Ενα" (without accent)', () => {
cy.getVs(id).search('Ενα').checkFirstOption('Ένα');
});

it('Greek: finds Αθήνα by normalized description ("Πρωτευουσα")', () => {
cy.getVs(id).search('Πρωτευουσα').checkFirstOption('Αθήνα');
});

// Cyrillic
it('Cyrillic: finds Ёжик when searching with ё (exact)', () => {
cy.getVs(id).search('Ёжик').checkFirstOption('Ёжик');
});

it('Cyrillic: finds Ёжик when searching "Ежик" (е instead of ё)', () => {
cy.getVs(id).search('Ежик').checkFirstOption('Ёжик');
});

it('Cyrillic: finds Ёжик by normalized description (зверёк → зверек)', () => {
cy.getVs(id).search('зверек').checkFirstOption('Ёжик');
});

// Vietnamese
it('Vietnamese: finds Việt Nam when searching "Viet Nam"', () => {
cy.getVs(id).search('Viet Nam').checkFirstOption('Việt Nam');
});

it('Vietnamese: finds Hà Nội when searching "Ha Noi"', () => {
cy.getVs(id).search('Ha Noi').checkFirstOption('Hà Nội');
});

// Chinese
it('Chinese: finds 北京 when searching "北京" (preserved as-is)', () => {
cy.getVs(id).search('北京').checkFirstOption('北京');
});

it('Chinese: finds 北京 by description "首都"', () => {
cy.getVs(id).search('首都').checkFirstOption('北京');
});

// Japanese
it('Japanese: finds 東京 when searching kanji', () => {
cy.getVs(id).search('東京').checkFirstOption('東京');
});

it('Japanese: finds カタカナ (katakana preserved as-is)', () => {
cy.getVs(id).search('カタカナ').checkFirstOption('カタカナ');
});

// Korean
it('Korean: finds 서울 (NFD-normalized symmetrically)', () => {
cy.getVs(id).search('서울').checkFirstOption('서울');
});

it('Korean: finds 한국어', () => {
cy.getVs(id).search('한국어').checkFirstOption('한국어');
});

// Arabic
it('Arabic: finds مُرَحَّباً when searching "مرحبا" (tashkeel stripped)', () => {
cy.getVs(id).search('مرحبا').checkFirstOption('مُرَحَّباً');
});

// Thai
it('Thai: finds กรุงเทพ when searching exact text', () => {
cy.getVs(id).search('กรุงเทพ').checkFirstOption('กรุงเทพ');
});

// Negative case
it('does not find non-existent text', () => {
cy.getVs(id).search('zzznotfound').hasNoOptions().close();
});
});

describe('Multi-language search with searchNormalize: false', () => {
const id = 'multi-language-search-no-normalize-select';

it('go to section', () => {
cy.goToSection('Multi-language search normalize');
});

// Exact matches — should work
it('Latin: finds Crème brûlée only when searching with diacritics', () => {
cy.open(id).search('Crème').checkFirstOption('Crème brûlée');
});

it('Greek: finds Ένα when searching exact "Ένα"', () => {
cy.getVs(id).search('Ένα').checkFirstOption('Ένα');
});

it('Cyrillic: finds Ёжик when searching exact "Ёжик"', () => {
cy.getVs(id).search('Ёжик').checkFirstOption('Ёжик');
});

it('Chinese: finds 北京 (no normalization needed)', () => {
cy.getVs(id).search('北京').checkFirstOption('北京');
});

it('Japanese: finds 東京 (no normalization needed)', () => {
cy.getVs(id).search('東京').checkFirstOption('東京');
});

Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new describe blocks are missing the usual go to section setup, making them order-dependent (they only work if a previous test already navigated to the correct docs section). To keep the suite robust and consistent with the rest of the file, add a cy.goToSection(...) in a beforeEach/first it for this block (and the other new blocks below).

Suggested change
beforeEach(() => {
cy.goToSection('Greek and Cyrillic search normalize');
});

Copilot uses AI. Check for mistakes.
it('Korean: finds 서울 (no normalization needed)', () => {
cy.getVs(id).search('서울').checkFirstOption('서울');
});

// Negative cases — without normalize, accent-stripped searches should not match
it('Latin: does NOT find Crème brûlée when searching "creme" (no normalize)', () => {
cy.getVs(id).search('creme').hasNoOptions();
});

it('German: does NOT find München when searching "Munchen"', () => {
cy.getVs(id).search('Munchen').hasNoOptions();
});

it('Swedish: does NOT find Göteborg when searching "Goteborg"', () => {
cy.getVs(id).search('Goteborg').hasNoOptions();
});

it('Finnish: does NOT find Jyväskylä when searching "Jyvaskyla"', () => {
cy.getVs(id).search('Jyvaskyla').hasNoOptions();
});

it('Greek: does NOT find Ένα when searching "Ενα" (no accent)', () => {
cy.getVs(id).search('Ενα').hasNoOptions();
});

it('Cyrillic: does NOT find Ёжик when searching "Ежик" (е instead of ё)', () => {
cy.getVs(id).search('Ежик').hasNoOptions();
});

it('Vietnamese: does NOT find Việt Nam when searching "Viet Nam"', () => {
cy.getVs(id).search('Viet Nam').hasNoOptions();
});

it('Arabic: does NOT find مُرَحَّباً when searching "مرحبا" (tashkeel mismatch)', () => {
cy.getVs(id).search('مرحبا').hasNoOptions().close();
});
});

describe('Multi-language tags variant with searchNormalize: true', () => {
const id = 'multi-language-tags-search-select';

it('go to section', () => {
cy.goToSection('Multi-language search normalize');
});

it('finds München with normalized search "Munchen"', () => {
cy.open(id).search('Munchen').checkFirstOption('München');
});

it('selects München and renders it as a value tag', () => {
cy.getVs(id).selectOption('munchen');
cy.getVs(id).hasValueTags(['München']);
});

it('finds Việt Nam with normalized search and adds a second tag', () => {
cy.getVs(id).search('Viet Nam').checkFirstOption('Việt Nam');
cy.getVs(id).selectOption('vietnam');
cy.getVs(id).hasValueTags(['München', 'Việt Nam']);
cy.getVs(id).checkValueTagsCount(2);
});

it('finds Ёжик with normalized search "Ежик" and adds Cyrillic tag', () => {
cy.getVs(id).search('Ежик').checkFirstOption('Ёжик');
cy.getVs(id).selectOption('yozhik');
cy.getVs(id).hasValueTags(['München', 'Việt Nam', 'Ёжик']);
cy.getVs(id).checkValueTagsCount(3);
});

it('removes a value tag and reduces tag count', () => {
cy.getVs(id).removeValueTag('München').checkValueTagsCount(2);
});

it('finds CJK options as-is (Chinese 北京)', () => {
cy.getVs(id).search('北京').checkFirstOption('北京');
cy.getVs(id).selectOption('beijing');
cy.getVs(id).checkValueTagsCount(3).close();
});
});

describe('Multi-language tags variant with searchNormalize: false', () => {
const id = 'multi-language-tags-search-no-normalize-select';

it('go to section', () => {
cy.goToSection('Multi-language search normalize');
});

it('does NOT find München when searching "Munchen" (no normalize)', () => {
cy.open(id).search('Munchen').hasNoOptions();
});

it('finds München only when searching with diacritics and tags it', () => {
cy.getVs(id).search('München').checkFirstOption('München');
cy.getVs(id).selectOption('munchen');
cy.getVs(id).hasValueTags(['München']);
cy.getVs(id).checkValueTagsCount(1);
});

it('finds Ёжик only with exact ё (not "Ежик")', () => {
cy.getVs(id).search('Ежик').hasNoOptions();
cy.getVs(id).search('Ёжик').checkFirstOption('Ёжик');
cy.getVs(id).selectOption('yozhik');
cy.getVs(id).hasValueTags(['München', 'Ёжик']);
cy.getVs(id).checkValueTagsCount(2).close();
});
});

describe('Multi-language popup variant with searchNormalize: true', () => {
const id = 'multi-language-popup-search-select';

it('go to section', () => {
cy.goToSection('Multi-language search normalize');
});

it('opens as a popup and finds München with normalized search', () => {
cy.open(id).search('Munchen').checkFirstOption('München');
});

it('finds Việt Nam with normalized search inside popup', () => {
cy.getVs(id).search('Viet Nam').checkFirstOption('Việt Nam');
});

it('finds Ёжик with normalized search "Ежик" inside popup', () => {
cy.getVs(id).search('Ежик').checkFirstOption('Ёжик');
});

it('finds CJK options as-is (Japanese 東京)', () => {
cy.getVs(id).search('東京').checkFirstOption('東京');
cy.closePopup(id);
});
});

describe('Multi-language popup variant with searchNormalize: false', () => {
const id = 'multi-language-popup-search-no-normalize-select';

it('go to section', () => {
cy.goToSection('Multi-language search normalize');
});

it('opens as a popup and does NOT find München with normalized search', () => {
cy.open(id).search('Munchen').hasNoOptions();
});

it('finds München only when searching with diacritics inside popup', () => {
cy.getVs(id).search('München').checkFirstOption('München');
});

it('CJK options still match as-is (no normalization needed)', () => {
cy.getVs(id).search('서울').checkFirstOption('서울');
cy.closePopup(id);
});
});

describe('Latin diacritics regression with normalize', () => {
const normalizedId = 'with-description-normalized-search-select';

it('go to section', () => {
cy.goToSection('Description search normalize');
});

it('still normalizes Latin descriptions (brulee finds brûlée)', () => {
cy.open(normalizedId).search('brulee').checkFirstOption('Beta');
});

it('still normalizes Latin descriptions (cafe finds café)', () => {
cy.getVs(normalizedId).search('cafe').checkFirstOption('Alpha');
});

it('still normalizes Latin descriptions (nino finds niño)', () => {
cy.getVs(normalizedId).search('nino').checkFirstOption('Gamma').close();
});
});

// // // // //
// // // // // Show dropbox as popup
// // // // //

describe('Show dropbox as popup - Clear search text', () => {
const id = 'multiple-show-as-popup-select';

Expand Down
2 changes: 1 addition & 1 deletion dist-archive/virtual-select-1.1.5.min.js

Large diffs are not rendered by default.

Loading