< Summary

Class:DropdownComponentView
Assembly:UIComponents
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/UIComponents/Scripts/Components/Dropdown/DropdownComponentView.cs
Covered lines:170
Uncovered lines:18
Coverable lines:188
Total lines:514
Line coverage:90.4% (170 of 188)
Covered branches:0
Total branches:0
Covered methods:28
Total methods:30
Method coverage:93.3% (28 of 30)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Awake()0%110100%
Configure(...)0%110100%
RefreshControl()0%22090%
Open()0%110100%
Close()0%110100%
SetTitle(...)0%2.032080%
SetOptions(...)0%330100%
FilterOptions(...)0%550100%
GetOption(...)0%20400%
GetOption(...)0%2.152066.67%
GetAllOptions()0%330100%
SetSelectAll(...)0%330100%
SetSearchPlaceHolderText(...)0%2.032080%
SetSearchNotFoundText(...)0%2.032080%
SetEmptyContentText(...)0%2.032080%
SetLoadingActive(...)0%2.062075%
SetSelectAllOptionActive(...)0%330100%
SetOptionsPanelHeightAsDynamic(...)0%110100%
Dispose()0%110100%
SelectOption(...)0%7.127086.67%
ToggleOptionsList()0%220100%
CreateSelectAllOption()0%110100%
CreateOption(...)0%3.013091.67%
OnOptionSelected(...)0%10.0110094.74%
UpdateSelectAllOptionStatus()0%550100%
RemoveAllInstantiatedOptions()0%110100%
RefreshOptionsPanelSize()0%6.046090%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/UIComponents/Scripts/Components/Dropdown/DropdownComponentView.cs

#LineLine coverage
 1using DCL.Helpers;
 2using System;
 3using System.Collections;
 4using System.Collections.Generic;
 5using System.Linq;
 6using TMPro;
 7using UnityEngine;
 8using UnityEngine.UI;
 9
 10public interface IDropdownComponentView
 11{
 12    /// <summary>
 13    /// Event that will be triggered when the selection of any option changes.
 14    /// </summary>
 15    event Action<bool, string, string> OnOptionSelectionChanged;
 16
 17    /// <summary>
 18    /// Set the dropdown as multiselect or not.
 19    /// </summary>
 20    bool isMultiselect { get; set; }
 21
 22    /// <summary>
 23    /// Open the options list.
 24    /// </summary>
 25    void Open();
 26
 27    /// <summary>
 28    /// Closes the options list
 29    /// </summary>
 30    void Close();
 31
 32    /// <summary>
 33    /// Set the dropdown title.
 34    /// </summary>
 35    /// <param name="newText">New title.</param>
 36    void SetTitle(string newText);
 37
 38    /// <summary>
 39    /// Set the available options of the dropdown.
 40    /// </summary>
 41    /// <param name="options">List of options..</param>
 42    void SetOptions(List<ToggleComponentModel> options);
 43
 44    /// <summary>
 45    /// Get an option of the dropdown.
 46    /// </summary>
 47    /// <param name="index">Index of the list of options.</param>
 48    /// <returns>A specific option toggle.</returns>
 49    IToggleComponentView GetOption(int index);
 50
 51    /// <summary>
 52    /// Get all the options of the dropdown.
 53    /// </summary>
 54    /// <returns>The list of options.</returns>
 55    List<IToggleComponentView> GetAllOptions();
 56
 57    /// <summary>
 58    /// Filter options using a text.
 59    /// </summary>
 60    /// <param name="filterText">Text used to filter.</param>
 61    void FilterOptions(string filterText);
 62
 63    /// <summary>
 64    /// Select/unselect all the available options (if multiselect is activated).
 65    /// </summary>
 66    void SetSelectAll(bool isSelected);
 67
 68    /// <summary>
 69    /// Set the search bar place holder text.
 70    /// </summary>
 71    /// <param name="newText">New text.</param>
 72    void SetSearchPlaceHolderText(string newText);
 73
 74    /// <summary>
 75    /// Set the text for when the search doesn't find anything.
 76    /// </summary>
 77    /// <param name="newText">New text.</param>
 78    void SetSearchNotFoundText(string newText);
 79
 80    /// <summary>
 81    /// Set the text for when the dropdown doesn't have items.
 82    /// </summary>
 83    /// <param name="newText">New text.</param>
 84    void SetEmptyContentText(string newText);
 85
 86    /// <summary>
 87    /// Show/Hide the loading panel.
 88    /// </summary>
 89    /// <param name="isActive">Tru for showing it.</param>
 90    void SetLoadingActive(bool isActive);
 91
 92    /// <summary>
 93    /// Show/Hide the "Select All" option (only for multiselect configuration).
 94    /// </summary>
 95    /// <param name="isActive"></param>
 96    void SetSelectAllOptionActive(bool isActive);
 97
 98    /// <summary>
 99    /// Make the height of the options panel be dynamic depending on the number of instantiated options.
 100    /// </summary>
 101    /// <param name="isDynamic">True for making it dynamic.</param>
 102    /// <param name="maxHeight">Max height to apply.</param>
 103    void SetOptionsPanelHeightAsDynamic(bool isDynamic, float maxHeight);
 104}
 105public class DropdownComponentView : BaseComponentView, IDropdownComponentView, IComponentModelConfig<DropdownComponentM
 106{
 107    internal const string SELECT_ALL_OPTION_ID = "select_all";
 108    internal const string SELECT_ALL_OPTION_TEXT = "Select All";
 109    internal const float BOTTOM_MARGIN_SIZE = 40f;
 110
 111    [Header("Prefab References")]
 112    [SerializeField] internal Button button;
 113    [SerializeField] internal TMP_Text title;
 114    [SerializeField] internal GameObject searchBarContainer;
 115    [SerializeField] internal SearchBarComponentView searchBar;
 116    [SerializeField] internal GameObject optionsPanel;
 117    [SerializeField] internal Image contentMaskImage;
 118    [SerializeField] internal GameObject loadingPanel;
 119    [SerializeField] internal RectTransform availableOptionsParent;
 120    [SerializeField] internal GridContainerComponentView availableOptions;
 121    [SerializeField] internal TMP_Text emptyContentMessage;
 122    [SerializeField] internal TMP_Text searchNotFoundMessage;
 123    [SerializeField] internal UIHelper_ClickBlocker blocker;
 124
 125    [Header("Resources")]
 126    [SerializeField] internal ToggleComponentView togglePrefab;
 127
 128    [Header("Configuration")]
 129    [SerializeField] internal DropdownComponentModel model;
 130
 0131    public string Title => title.text;
 132
 133    public event Action<bool, string, string> OnOptionSelectionChanged;
 134
 135    internal ToggleComponentView selectAllOptionComponent;
 136    internal Coroutine refreshOptionsPanelCoroutine;
 137
 138    public bool isMultiselect
 139    {
 1883140        get => model.isMultiselect;
 332141        set => model.isMultiselect = value;
 142    }
 143
 144    internal bool isOpen = false;
 145    internal List<ToggleComponentModel> originalOptions;
 146
 147    public override void Awake()
 148    {
 329149        base.Awake();
 150
 329151        RefreshControl();
 329152        Close();
 153
 329154        blocker.OnClicked += Close;
 329155        button.onClick.AddListener(() => ToggleOptionsList());
 329156        searchBar.OnSearchText += FilterOptions;
 329157    }
 158
 159    public void Configure(DropdownComponentModel newModel)
 160    {
 1161        model = newModel;
 1162        RefreshControl();
 1163    }
 164
 165    public override void RefreshControl()
 166    {
 330167        if (model == null)
 0168            return;
 169
 330170        isMultiselect = model.isMultiselect;
 330171        SetTitle(model.title);
 330172        SetOptions(model.options);
 330173        SetSearchPlaceHolderText(model.searchPlaceHolderText);
 330174        SetSearchNotFoundText(model.searchNotFoundText);
 330175        SetEmptyContentText(model.emptyContentText);
 330176        SetOptionsPanelHeightAsDynamic(model.isOptionsPanelHeightDynamic, model.maxValueForDynamicHeight);
 330177    }
 178
 179    public void Open()
 180    {
 4181        optionsPanel.SetActive(true);
 4182        isOpen = true;
 4183        blocker.Activate();
 4184        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 4185        refreshOptionsPanelCoroutine = CoroutineStarter.Start(RefreshOptionsPanelSize());
 4186    }
 187
 188    public void Close()
 189    {
 352190        optionsPanel.SetActive(false);
 352191        isOpen = false;
 352192        searchBar.ClearSearch();
 352193        blocker.Deactivate();
 352194    }
 195
 196    public void SetTitle(string newText)
 197    {
 949198        model.title = newText;
 199
 949200        if (title == null)
 0201            return;
 202
 949203        title.text = newText;
 949204    }
 205
 206    public void SetOptions(List<ToggleComponentModel> options)
 207    {
 616208        model.options = options;
 616209        originalOptions = options;
 210
 616211        RemoveAllInstantiatedOptions();
 212
 616213        if (options.Count > 0)
 381214            CreateSelectAllOption();
 215
 3956216        for (int i = 0; i < options.Count; i++)
 217        {
 1362218            CreateOption(options[i], $"Option_{i}");
 219        }
 220
 616221        UpdateSelectAllOptionStatus();
 616222        SetSelectAllOptionActive(model.showSelectAllOption);
 223
 616224        searchBarContainer.SetActive(options.Count > 0);
 616225        contentMaskImage.enabled = options.Count > 0;
 616226        emptyContentMessage.gameObject.SetActive(options.Count == 0);
 227
 616228        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 616229        refreshOptionsPanelCoroutine = CoroutineStarter.Start(RefreshOptionsPanelSize());
 616230    }
 231
 232    public void FilterOptions(string filterText)
 233    {
 24234        if (filterText == string.Empty)
 235        {
 23236            searchNotFoundMessage.gameObject.SetActive(false);
 23237            SetOptions(originalOptions);
 23238            return;
 239        }
 240
 1241        List<ToggleComponentModel> newFilteredOptions = new List<ToggleComponentModel>();
 8242        foreach (ToggleComponentModel option in originalOptions)
 243        {
 3244            if (option.text.ToLower().Contains(filterText.ToLower()))
 1245                newFilteredOptions.Add(option);
 246        }
 247
 1248        model.options = newFilteredOptions;
 249
 1250        RemoveAllInstantiatedOptions();
 251
 4252        for (int i = 0; i < newFilteredOptions.Count; i++)
 253        {
 1254            CreateOption(newFilteredOptions[i], $"FilteredOption_{i}");
 255        }
 256
 1257        searchNotFoundMessage.gameObject.SetActive(newFilteredOptions.Count == 0);
 258
 1259        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 1260        refreshOptionsPanelCoroutine = CoroutineStarter.Start(RefreshOptionsPanelSize());
 1261    }
 262
 263    public IToggleComponentView GetOption(string id)
 264    {
 0265        foreach (var component in availableOptions.GetItems())
 266        {
 0267            if (component is not IToggleComponentView toggle) continue;
 0268            if (toggle.id == id) return toggle;
 269        }
 270
 0271        return null;
 0272    }
 273
 274    public IToggleComponentView GetOption(int index)
 275    {
 32276        if (index >= availableOptions.GetItems().Count - 1)
 0277            return null;
 278
 32279        return availableOptions.GetItems()[index + 1] as IToggleComponentView;
 280    }
 281
 282    public List<IToggleComponentView> GetAllOptions()
 283    {
 64284        return availableOptions
 285            .GetItems()
 132286            .Select(x => x as IToggleComponentView)
 132287            .Where(x => x.id != SELECT_ALL_OPTION_ID)
 288            .ToList();
 289    }
 290
 291    public void SetSelectAll(bool isSelected)
 292    {
 2293        List<IToggleComponentView> allOptions = GetAllOptions();
 12294        foreach (IToggleComponentView option in allOptions)
 295        {
 4296            option.isOn = isSelected;
 297        }
 298
 12299        foreach (ToggleComponentModel option in originalOptions)
 300        {
 4301            option.isOn = isSelected;
 302        }
 2303    }
 304
 305    public void SetSearchPlaceHolderText(string newText)
 306    {
 331307        model.searchPlaceHolderText = newText;
 308
 331309        if (searchBar == null)
 0310            return;
 311
 331312        searchBar.SetPlaceHolderText(newText);
 331313    }
 314
 315    public void SetSearchNotFoundText(string newText)
 316    {
 331317        model.searchNotFoundText = newText;
 318
 331319        if (searchNotFoundMessage == null)
 0320            return;
 321
 331322        searchNotFoundMessage.text = newText;
 331323    }
 324
 325    public void SetEmptyContentText(string newText)
 326    {
 331327        model.emptyContentText = newText;
 328
 331329        if (emptyContentMessage == null)
 0330            return;
 331
 331332        emptyContentMessage.text = newText;
 331333    }
 334
 335    public void SetLoadingActive(bool isActive)
 336    {
 2337        if (loadingPanel == null)
 0338            return;
 339
 2340        loadingPanel.SetActive(isActive);
 2341    }
 342
 343    public void SetSelectAllOptionActive(bool isActive)
 344    {
 618345        model.showSelectAllOption = isActive;
 346
 618347        if (!isMultiselect || selectAllOptionComponent == null)
 604348            return;
 349
 14350        selectAllOptionComponent.gameObject.SetActive(isActive);
 14351    }
 352
 353    public void SetOptionsPanelHeightAsDynamic(bool isDynamic, float maxHeight)
 354    {
 332355        model.isOptionsPanelHeightDynamic = isDynamic;
 332356        model.maxValueForDynamicHeight = maxHeight;
 332357        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 332358        refreshOptionsPanelCoroutine = CoroutineStarter.Start(RefreshOptionsPanelSize());
 332359    }
 360
 361    public override void Dispose()
 362    {
 355363        base.Dispose();
 364
 355365        blocker.OnClicked -= Close;
 355366        button.onClick.RemoveAllListeners();
 355367        searchBar.OnSearchText -= FilterOptions;
 368
 355369        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 355370    }
 371
 372    public IToggleComponentView SelectOption(string id, bool notify)
 373    {
 354374        IToggleComponentView selectedOption = null;
 375
 1314376        foreach (BaseComponentView component in availableOptions.GetItems())
 377        {
 303378            if (component is not IToggleComponentView toggle) continue;
 379
 303380            if (toggle.id == id)
 381            {
 101382                if (notify)
 0383                    toggle.isOn = true;
 384                else
 101385                    toggle.SetIsOnWithoutNotify(true);
 386
 101387                selectedOption = toggle;
 388            }
 202389            else if (!isMultiselect)
 390            {
 202391                if (notify)
 0392                    toggle.isOn = false;
 393                else
 202394                    toggle.SetIsOnWithoutNotify(false);
 395            }
 396        }
 397
 354398        return selectedOption;
 399    }
 400
 401    internal void ToggleOptionsList()
 402    {
 2403        if (isOpen)
 1404            Close();
 405        else
 1406            Open();
 1407    }
 408
 409    internal void CreateSelectAllOption()
 410    {
 382411        CreateOption(
 412            new ToggleComponentModel
 413            {
 414                id = SELECT_ALL_OPTION_ID,
 415                text = SELECT_ALL_OPTION_TEXT,
 416                isOn = false,
 417                isTextActive = true
 418            },
 419            $"Option_{SELECT_ALL_OPTION_ID}");
 382420    }
 421
 422    internal void CreateOption(ToggleComponentModel newOptionModel, string name)
 423    {
 1746424        if (togglePrefab == null)
 0425            return;
 426
 1746427        ToggleComponentView newGO = Instantiate(togglePrefab);
 1746428        newGO.gameObject.SetActive(true);
 1746429        newGO.Configure(newOptionModel);
 1746430        availableOptions.AddItemWithResize(newGO);
 1746431        newGO.name = name;
 432
 1746433        if (newOptionModel.id == SELECT_ALL_OPTION_ID)
 434        {
 382435            selectAllOptionComponent = newGO;
 382436            newGO.gameObject.SetActive(isMultiselect);
 437        }
 438
 1746439        newGO.OnSelectedChanged += OnOptionSelected;
 1746440    }
 441
 442    internal void OnOptionSelected(bool isOn, string optionId, string optionName)
 443    {
 25444        if (optionId != SELECT_ALL_OPTION_ID)
 445        {
 25446            OnOptionSelectionChanged?.Invoke(isOn, optionId, optionName);
 447
 25448            if (isOn && !isMultiselect)
 449            {
 11450                List<IToggleComponentView> allOptions = GetAllOptions();
 124451                foreach (IToggleComponentView option in allOptions)
 452                {
 51453                    if (option.id != optionId)
 40454                        option.isOn = false;
 455                }
 456            }
 457
 260458            foreach (ToggleComponentModel option in originalOptions)
 459            {
 105460                if (optionId == option.id)
 25461                    option.isOn = isOn;
 462            }
 463
 25464            UpdateSelectAllOptionStatus();
 465        }
 466        else
 467        {
 0468            SetSelectAll(isOn);
 469        }
 470
 25471        if (!isMultiselect)
 19472            Close();
 25473    }
 474
 475    internal void UpdateSelectAllOptionStatus()
 476    {
 641477        if (!isMultiselect)
 593478            return;
 479
 48480        List<IToggleComponentView> allOptions = GetAllOptions();
 481
 48482        if (selectAllOptionComponent != null)
 483        {
 18484            selectAllOptionComponent.OnSelectedChanged -= OnOptionSelected;
 39485            selectAllOptionComponent.isOn = allOptions.Count > 0 && allOptions.All(x => x.isOn);
 18486            selectAllOptionComponent.OnSelectedChanged += OnOptionSelected;
 487        }
 48488    }
 489
 490    internal void RemoveAllInstantiatedOptions()
 491    {
 618492        availableOptions.RemoveItems();
 618493    }
 494
 495    internal IEnumerator RefreshOptionsPanelSize()
 496    {
 953497        if (!model.isOptionsPanelHeightDynamic)
 774498            yield break;
 499
 179500        yield return null;
 501
 3502        Utils.ForceRebuildLayoutImmediate(availableOptionsParent);
 503
 3504        yield return null;
 505
 2506        if (optionsPanel == null)
 0507            yield break;
 508
 2509        RectTransform optionsPanelTransform = optionsPanel.transform as RectTransform;
 2510        optionsPanelTransform.sizeDelta = new Vector2(
 511            optionsPanelTransform.sizeDelta.x,
 512            Mathf.Clamp(availableOptionsParent.sizeDelta.y + BOTTOM_MARGIN_SIZE, 0f, model.maxValueForDynamicHeight));
 2513    }
 514}