@@ -866,6 +866,126 @@ describe('AttributesSidebar', () => {
866866 } ) ;
867867 } ) ;
868868
869+ describe ( 'Favorites First in Scope Tabs' , ( ) => {
870+ it ( 'should show favorites first in Resource tab' , ( ) => {
871+ mockUseFavoriteAttributes . mockReturnValue ( {
872+ favoriteAttributes : [ 'resource.namespace' , 'resource.cluster' ] , // These are favorites
873+ toggleFavorite : jest . fn ( ) ,
874+ reorderFavorites : jest . fn ( ) ,
875+ } ) ;
876+
877+ const { container } = render (
878+ < AttributesSidebar
879+ options = { sampleOptions }
880+ selected = { undefined }
881+ onAttributeChange = { mockOnAttributeChange }
882+ model = { mockModel }
883+ showFavorites = { true }
884+ />
885+ ) ;
886+
887+ fireEvent . click ( screen . getByText ( 'Resource' ) ) ;
888+
889+ const items = container . querySelectorAll ( 'li[title]' ) ;
890+ const labels = Array . from ( items ) . map ( ( item ) => item . textContent ) ;
891+
892+ // Favorites should be first (namespace, cluster), then non-favorites alphabetically (service.name)
893+ expect ( labels [ 0 ] ) . toContain ( 'namespace' ) ;
894+ expect ( labels [ 1 ] ) . toContain ( 'cluster' ) ;
895+ expect ( labels [ 2 ] ) . toContain ( 'service.name' ) ;
896+ } ) ;
897+
898+ it ( 'should show favorites first in Span tab' , ( ) => {
899+ mockUseFavoriteAttributes . mockReturnValue ( {
900+ favoriteAttributes : [ 'span.db.system' , 'span.http.method' ] , // These are favorites
901+ toggleFavorite : jest . fn ( ) ,
902+ reorderFavorites : jest . fn ( ) ,
903+ } ) ;
904+
905+ const { container } = render (
906+ < AttributesSidebar
907+ options = { sampleOptions }
908+ selected = { undefined }
909+ onAttributeChange = { mockOnAttributeChange }
910+ model = { mockModel }
911+ showFavorites = { true }
912+ />
913+ ) ;
914+
915+ fireEvent . click ( screen . getByText ( 'Span' ) ) ;
916+
917+ const items = container . querySelectorAll ( 'li[title]' ) ;
918+ const labels = Array . from ( items ) . map ( ( item ) => item . textContent ) ;
919+
920+ // Favorites should be first in custom order (db.system, http.method), then non-favorites (http.status_code)
921+ expect ( labels [ 0 ] ) . toContain ( 'db.system' ) ;
922+ expect ( labels [ 1 ] ) . toContain ( 'http.method' ) ;
923+ expect ( labels [ 2 ] ) . toContain ( 'http.status_code' ) ;
924+ } ) ;
925+
926+ it ( 'should NOT show favorites first in All tab' , ( ) => {
927+ mockUseFavoriteAttributes . mockReturnValue ( {
928+ favoriteAttributes : [ 'span.db.system' , 'resource.namespace' ] ,
929+ toggleFavorite : jest . fn ( ) ,
930+ reorderFavorites : jest . fn ( ) ,
931+ } ) ;
932+
933+ const { container } = render (
934+ < AttributesSidebar
935+ options = { sampleOptions }
936+ selected = { undefined }
937+ onAttributeChange = { mockOnAttributeChange }
938+ model = { mockModel }
939+ showFavorites = { true }
940+ />
941+ ) ;
942+
943+ fireEvent . click ( screen . getByText ( 'All' ) ) ;
944+
945+ const items = container . querySelectorAll ( 'li[title]' ) ;
946+ const labels = Array . from ( items ) . map ( ( item ) => item . textContent ) ;
947+
948+ // Should be in alphabetical order, not favorites first
949+ // Alphabetical: cluster, db.system, http.method, http.status_code, namespace, service.name
950+ expect ( labels [ 0 ] ) . toContain ( 'cluster' ) ;
951+ expect ( labels [ 1 ] ) . toContain ( 'db.system' ) ;
952+ expect ( labels [ 2 ] ) . toContain ( 'http.method' ) ;
953+ } ) ;
954+
955+ it ( 'should apply search filter to favorites-first ordering in Resource tab' , ( ) => {
956+ mockUseFavoriteAttributes . mockReturnValue ( {
957+ favoriteAttributes : [ 'resource.cluster' , 'resource.namespace' ] ,
958+ toggleFavorite : jest . fn ( ) ,
959+ reorderFavorites : jest . fn ( ) ,
960+ } ) ;
961+
962+ const { container } = render (
963+ < AttributesSidebar
964+ options = { sampleOptions }
965+ selected = { undefined }
966+ onAttributeChange = { mockOnAttributeChange }
967+ model = { mockModel }
968+ showFavorites = { true }
969+ />
970+ ) ;
971+
972+ fireEvent . click ( screen . getByText ( 'Resource' ) ) ;
973+
974+ // Search for 'c'
975+ const searchInput = screen . getByPlaceholderText ( 'Search attributes...' ) ;
976+ fireEvent . change ( searchInput , { target : { value : 'c' } } ) ;
977+
978+ const items = container . querySelectorAll ( 'li[title]' ) ;
979+ const labels = Array . from ( items ) . map ( ( item ) => item . textContent ) ;
980+
981+ // Should show favorites first (both contain 'c'), then non-favorites
982+ // 'cluster' contains 'c', 'namespace' contains 'c' at the end, both are favorites
983+ expect ( labels [ 0 ] ) . toContain ( 'cluster' ) ;
984+ expect ( labels [ 1 ] ) . toContain ( 'namespace' ) ;
985+ expect ( labels [ 2 ] ) . toContain ( 'service.name' ) ; // non-favorite that contains 'c'
986+ } ) ;
987+ } ) ;
988+
869989 describe ( 'Edge Cases' , ( ) => {
870990 it ( 'should handle empty options array' , ( ) => {
871991 render (
0 commit comments