@@ -3685,3 +3685,270 @@ describe("Case-Insensitive Selection", () => {
36853685 cy . get ( "[ui5-cb-item]" ) . eq ( 1 ) . should ( "have.prop" , "selected" , true ) ;
36863686 } ) ;
36873687} ) ;
3688+
3689+ describe ( "Highlighting" , ( ) => {
3690+ it ( "should highlight first match when typing" , ( ) => {
3691+ cy . mount (
3692+ < ComboBox >
3693+ < ComboBoxItem text = "Argentina" > </ ComboBoxItem >
3694+ < ComboBoxItem text = "South Africa" > </ ComboBoxItem >
3695+ < ComboBoxItem text = "Bulgaria" > </ ComboBoxItem >
3696+ </ ComboBox >
3697+ ) ;
3698+
3699+ cy . get ( "[ui5-combobox]" )
3700+ . as ( "combobox" )
3701+ . shadow ( )
3702+ . find ( "input" )
3703+ . as ( "input" ) ;
3704+
3705+ // Type "A" - should highlight first word starting with "A"
3706+ cy . get ( "@input" ) . realClick ( ) ;
3707+ cy . get ( "@input" ) . realType ( "A" ) ;
3708+
3709+ // Check Argentina is highlighted
3710+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3711+ . should ( "contain.html" , "<b>A</b>" ) ;
3712+
3713+ // Check South Africa is highlighted (second word)
3714+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 1 ) . shadow ( ) . find ( ".ui5-li-title" )
3715+ . should ( "contain.html" , "<b>A</b>" ) ;
3716+ } ) ;
3717+
3718+ it ( "should highlight with StartsWithPerTerm pattern regardless of filter mode" , ( ) => {
3719+ cy . mount (
3720+ < ComboBox filter = "Contains" >
3721+ < ComboBoxItem text = "Bosnia and Herzegovina" > </ ComboBoxItem >
3722+ < ComboBoxItem text = "South Africa" > </ ComboBoxItem >
3723+ </ ComboBox >
3724+ ) ;
3725+
3726+ cy . get ( "[ui5-combobox]" )
3727+ . as ( "combobox" )
3728+ . shadow ( )
3729+ . find ( "input" )
3730+ . as ( "input" ) ;
3731+
3732+ // Type "Her" - with Contains filter, both items should show
3733+ // But highlighting should use StartsWithPerTerm (first word starting with "Her")
3734+ cy . get ( "@input" ) . realClick ( ) ;
3735+ cy . get ( "@input" ) . realType ( "Her" ) ;
3736+
3737+ // Herzegovina should be highlighted (word starts with "Her")
3738+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3739+ . should ( "contain.html" , "<b>Her</b>" ) ;
3740+ } ) ;
3741+
3742+ it ( "should highlight grouped items" , ( ) => {
3743+ cy . mount (
3744+ < ComboBox >
3745+ < ComboBoxItemGroup header-text = "Group A" >
3746+ < ComboBoxItem text = "Argentina" > </ ComboBoxItem >
3747+ < ComboBoxItem text = "Australia" > </ ComboBoxItem >
3748+ </ ComboBoxItemGroup >
3749+ < ComboBoxItemGroup header-text = "Group B" >
3750+ < ComboBoxItem text = "South Africa" > </ ComboBoxItem >
3751+ < ComboBoxItem text = "Brazil" > </ ComboBoxItem >
3752+ </ ComboBoxItemGroup >
3753+ </ ComboBox >
3754+ ) ;
3755+
3756+ cy . get ( "[ui5-combobox]" )
3757+ . as ( "combobox" )
3758+ . shadow ( )
3759+ . find ( "input" )
3760+ . realClick ( ) ;
3761+
3762+ cy . get ( "[ui5-combobox]" )
3763+ . shadow ( )
3764+ . find ( "input" )
3765+ . realType ( "A" ) ;
3766+
3767+ // Check both items in Group A are highlighted
3768+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item-group]" ) . eq ( 0 )
3769+ . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3770+ . should ( "contain.html" , "<b>A</b>" ) ;
3771+
3772+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item-group]" ) . eq ( 0 )
3773+ . find ( "[ui5-cb-item]" ) . eq ( 1 ) . shadow ( ) . find ( ".ui5-li-title" )
3774+ . should ( "contain.html" , "<b>A</b>" ) ;
3775+
3776+ // Check South Africa is highlighted (second word)
3777+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item-group]" ) . eq ( 1 )
3778+ . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3779+ . should ( "contain.html" , "<b>A</b>" ) ;
3780+ } ) ;
3781+
3782+ it ( "should handle special characters safely" , ( ) => {
3783+ cy . mount (
3784+ < ComboBox >
3785+ < ComboBoxItem text = "<script>alert('XSS')</script>" > </ ComboBoxItem >
3786+ < ComboBoxItem text = "Price: $100 & Up" > </ ComboBoxItem >
3787+ </ ComboBox >
3788+ ) ;
3789+
3790+ cy . get ( "[ui5-combobox]" )
3791+ . as ( "combobox" )
3792+ . shadow ( )
3793+ . find ( "input" )
3794+ . realClick ( ) ;
3795+
3796+ cy . get ( "[ui5-combobox]" )
3797+ . shadow ( )
3798+ . find ( "input" )
3799+ . realType ( "P" ) ;
3800+
3801+ // Special characters should be escaped, no XSS
3802+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 1 ) . shadow ( ) . find ( ".ui5-li-title" )
3803+ . should ( "contain.html" , "<b>P</b>rice: $100 & Up" ) ;
3804+
3805+ // Script tags should be escaped
3806+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3807+ . should ( "not.contain.html" , "<script>" ) ;
3808+ } ) ;
3809+
3810+ it ( "should only highlight text, not additionalText" , ( ) => {
3811+ cy . mount (
3812+ < ComboBox >
3813+ < ComboBoxItem text = "Argentina" additional-text = "AR" > </ ComboBoxItem >
3814+ < ComboBoxItem text = "Australia" additional-text = "AU" > </ ComboBoxItem >
3815+ </ ComboBox >
3816+ ) ;
3817+
3818+ cy . get ( "[ui5-combobox]" )
3819+ . as ( "combobox" )
3820+ . shadow ( )
3821+ . find ( "input" )
3822+ . realClick ( ) ;
3823+
3824+ cy . get ( "[ui5-combobox]" )
3825+ . shadow ( )
3826+ . find ( "input" )
3827+ . realType ( "A" ) ;
3828+
3829+ // Main text should be highlighted
3830+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3831+ . should ( "contain.html" , "<b>A</b>" ) ;
3832+
3833+ // Additional text should NOT be highlighted (no <b> tags)
3834+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-additional-text" )
3835+ . should ( "not.contain.html" , "<b>" ) ;
3836+ } ) ;
3837+
3838+ it ( "should clear highlighting when input is cleared" , ( ) => {
3839+ cy . mount (
3840+ < ComboBox showClearIcon >
3841+ < ComboBoxItem text = "Argentina" > </ ComboBoxItem >
3842+ < ComboBoxItem text = "Australia" > </ ComboBoxItem >
3843+ </ ComboBox >
3844+ ) ;
3845+
3846+ cy . get ( "[ui5-combobox]" )
3847+ . as ( "combobox" )
3848+ . shadow ( )
3849+ . find ( "input" )
3850+ . as ( "input" ) ;
3851+
3852+ // Type to get highlighting
3853+ cy . get ( "@input" ) . realClick ( ) ;
3854+ cy . get ( "@input" ) . realType ( "A" ) ;
3855+
3856+ // Should be highlighted
3857+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3858+ . should ( "contain.html" , "<b>A</b>" ) ;
3859+
3860+ // Clear input
3861+ cy . get ( "@combobox" ) . shadow ( ) . find ( ".ui5-input-clear-icon-wrapper" ) . realClick ( ) ;
3862+
3863+ // Open dropdown again to check items
3864+ cy . get ( "@combobox" ) . shadow ( ) . find ( "[ui5-icon]" ) . last ( ) . realClick ( ) ;
3865+
3866+ // Should not be highlighted anymore
3867+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3868+ . should ( "not.contain.html" , "<b>" ) ;
3869+ } ) ;
3870+
3871+ it ( "should highlight only the first match, not all occurrences" , ( ) => {
3872+ cy . mount (
3873+ < ComboBox >
3874+ < ComboBoxItem text = "New New York" > </ ComboBoxItem >
3875+ </ ComboBox >
3876+ ) ;
3877+
3878+ cy . get ( "[ui5-combobox]" )
3879+ . as ( "combobox" )
3880+ . shadow ( )
3881+ . find ( "input" )
3882+ . realClick ( ) ;
3883+
3884+ cy . get ( "[ui5-combobox]" )
3885+ . shadow ( )
3886+ . find ( "input" )
3887+ . realType ( "New" ) ;
3888+
3889+ // Should only highlight the first "New", not the second
3890+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3891+ . invoke ( "html" )
3892+ . then ( ( html ) => {
3893+ // Count <b> tags - should be exactly 1 pair
3894+ const openTags = ( html . match ( / < b > / g) || [ ] ) . length ;
3895+ const closeTags = ( html . match ( / < \/ b > / g) || [ ] ) . length ;
3896+ expect ( openTags ) . to . equal ( 1 ) ;
3897+ expect ( closeTags ) . to . equal ( 1 ) ;
3898+ } ) ;
3899+ } ) ;
3900+
3901+ it ( "should handle case-insensitive matching" , ( ) => {
3902+ cy . mount (
3903+ < ComboBox >
3904+ < ComboBoxItem text = "ARGENTINA" > </ ComboBoxItem >
3905+ < ComboBoxItem text = "argentina" > </ ComboBoxItem >
3906+ < ComboBoxItem text = "ArGeNtInA" > </ ComboBoxItem >
3907+ </ ComboBox >
3908+ ) ;
3909+
3910+ cy . get ( "[ui5-combobox]" )
3911+ . as ( "combobox" )
3912+ . shadow ( )
3913+ . find ( "input" )
3914+ . realClick ( ) ;
3915+
3916+ cy . get ( "[ui5-combobox]" )
3917+ . shadow ( )
3918+ . find ( "input" )
3919+ . realType ( "arg" ) ;
3920+
3921+ // All three should be highlighted (case-insensitive)
3922+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3923+ . should ( "contain.html" , "<b>ARG</b>" ) ;
3924+
3925+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 1 ) . shadow ( ) . find ( ".ui5-li-title" )
3926+ . should ( "contain.html" , "<b>arg</b>" ) ;
3927+
3928+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 2 ) . shadow ( ) . find ( ".ui5-li-title" )
3929+ . should ( "contain.html" , "<b>ArG</b>" ) ;
3930+ } ) ;
3931+
3932+ it ( "should preserve original case in highlighting" , ( ) => {
3933+ cy . mount (
3934+ < ComboBox >
3935+ < ComboBoxItem text = "South AFRICA" > </ ComboBoxItem >
3936+ </ ComboBox >
3937+ ) ;
3938+
3939+ cy . get ( "[ui5-combobox]" )
3940+ . as ( "combobox" )
3941+ . shadow ( )
3942+ . find ( "input" )
3943+ . realClick ( ) ;
3944+
3945+ cy . get ( "[ui5-combobox]" )
3946+ . shadow ( )
3947+ . find ( "input" )
3948+ . realType ( "afr" ) ;
3949+
3950+ // Should preserve original case "AFR" not "afr"
3951+ cy . get ( "@combobox" ) . find ( "[ui5-cb-item]" ) . eq ( 0 ) . shadow ( ) . find ( ".ui5-li-title" )
3952+ . should ( "contain.html" , "<b>AFR</b>" ) ;
3953+ } ) ;
3954+ } ) ;
0 commit comments