< Summary

Class:DropdownComponentView
Assembly:UIComponents
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/UIComponents/Scripts/Components/Dropdown/DropdownComponentView.cs
Covered lines:159
Uncovered lines:10
Coverable lines:169
Total lines:472
Line coverage:94% (159 of 169)
Covered branches:0
Total branches:0

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%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%
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
 131    public event Action<bool, string, string> OnOptionSelectionChanged;
 132
 133    internal ToggleComponentView selectAllOptionComponent;
 134    internal Coroutine refreshOptionsPanelCoroutine;
 135
 136    public bool isMultiselect
 137    {
 538138        get => model.isMultiselect;
 126139        set => model.isMultiselect = value;
 140    }
 141
 142    internal bool isOpen = false;
 143    internal List<ToggleComponentModel> originalOptions;
 144
 145    public override void Awake()
 146    {
 123147        base.Awake();
 148
 123149        RefreshControl();
 123150        Close();
 151
 123152        blocker.OnClicked += Close;
 123153        button.onClick.AddListener(() => ToggleOptionsList());
 123154        searchBar.OnSearchText += FilterOptions;
 123155    }
 156
 157    public void Configure(DropdownComponentModel newModel)
 158    {
 1159        model = newModel;
 1160        RefreshControl();
 1161    }
 162
 163    public override void RefreshControl()
 164    {
 124165        if (model == null)
 0166            return;
 167
 124168        isMultiselect = model.isMultiselect;
 124169        SetTitle(model.title);
 124170        SetOptions(model.options);
 124171        SetSearchPlaceHolderText(model.searchPlaceHolderText);
 124172        SetSearchNotFoundText(model.searchNotFoundText);
 124173        SetEmptyContentText(model.emptyContentText);
 124174        SetOptionsPanelHeightAsDynamic(model.isOptionsPanelHeightDynamic, model.maxValueForDynamicHeight);
 124175    }
 176
 177    public void Open()
 178    {
 4179        optionsPanel.SetActive(true);
 4180        isOpen = true;
 4181        blocker.Activate();
 4182        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 4183        refreshOptionsPanelCoroutine = CoroutineStarter.Start(RefreshOptionsPanelSize());
 4184    }
 185
 186    public void Close()
 187    {
 132188        optionsPanel.SetActive(false);
 132189        isOpen = false;
 132190        searchBar.ClearSearch();
 132191        blocker.Deactivate();
 132192    }
 193
 194    public void SetTitle(string newText)
 195    {
 180196        model.title = newText;
 197
 180198        if (title == null)
 0199            return;
 200
 180201        title.text = newText;
 180202    }
 203
 204    public void SetOptions(List<ToggleComponentModel> options)
 205    {
 196206        model.options = options;
 196207        originalOptions = options;
 208
 196209        RemoveAllInstantiatedOptions();
 210
 196211        if (options.Count > 0)
 117212            CreateSelectAllOption();
 213
 1072214        for (int i = 0; i < options.Count; i++)
 215        {
 340216            CreateOption(options[i], $"Option_{i}");
 217        }
 218
 196219        UpdateSelectAllOptionStatus();
 196220        SetSelectAllOptionActive(model.showSelectAllOption);
 221
 196222        searchBarContainer.SetActive(options.Count > 0);
 196223        contentMaskImage.enabled = options.Count > 0;
 196224        emptyContentMessage.gameObject.SetActive(options.Count == 0);
 225
 196226        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 196227        refreshOptionsPanelCoroutine = CoroutineStarter.Start(RefreshOptionsPanelSize());
 196228    }
 229
 230    public void FilterOptions(string filterText)
 231    {
 10232        if (filterText == string.Empty)
 233        {
 9234            searchNotFoundMessage.gameObject.SetActive(false);
 9235            SetOptions(originalOptions);
 9236            return;
 237        }
 238
 1239        List<ToggleComponentModel> newFilteredOptions = new List<ToggleComponentModel>();
 8240        foreach (ToggleComponentModel option in originalOptions)
 241        {
 3242            if (option.text.ToLower().Contains(filterText.ToLower()))
 1243                newFilteredOptions.Add(option);
 244        }
 245
 1246        model.options = newFilteredOptions;
 247
 1248        RemoveAllInstantiatedOptions();
 249
 4250        for (int i = 0; i < newFilteredOptions.Count; i++)
 251        {
 1252            CreateOption(newFilteredOptions[i], $"FilteredOption_{i}");
 253        }
 254
 1255        searchNotFoundMessage.gameObject.SetActive(newFilteredOptions.Count == 0);
 256
 1257        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 1258        refreshOptionsPanelCoroutine = CoroutineStarter.Start(RefreshOptionsPanelSize());
 1259    }
 260
 261    public IToggleComponentView GetOption(int index)
 262    {
 11263        if (index >= availableOptions.GetItems().Count - 1)
 0264            return null;
 265
 11266        return availableOptions.GetItems()[index + 1] as IToggleComponentView;
 267    }
 268
 269    public List<IToggleComponentView> GetAllOptions()
 270    {
 104271        return availableOptions
 272            .GetItems()
 78273            .Select(x => x as IToggleComponentView)
 78274            .Where(x => x.id != SELECT_ALL_OPTION_ID)
 275            .ToList();
 276    }
 277
 278    public void SetSelectAll(bool isSelected)
 279    {
 2280        List<IToggleComponentView> allOptions = GetAllOptions();
 12281        foreach (IToggleComponentView option in allOptions)
 282        {
 4283            option.isOn = isSelected;
 284        }
 285
 12286        foreach (ToggleComponentModel option in originalOptions)
 287        {
 4288            option.isOn = isSelected;
 289        }
 2290    }
 291
 292    public void SetSearchPlaceHolderText(string newText)
 293    {
 125294        model.searchPlaceHolderText = newText;
 295
 125296        if (searchBar == null)
 0297            return;
 298
 125299        searchBar.SetPlaceHolderText(newText);
 125300    }
 301
 302    public void SetSearchNotFoundText(string newText)
 303    {
 125304        model.searchNotFoundText = newText;
 305
 125306        if (searchNotFoundMessage == null)
 0307            return;
 308
 125309        searchNotFoundMessage.text = newText;
 125310    }
 311
 312    public void SetEmptyContentText(string newText)
 313    {
 125314        model.emptyContentText = newText;
 315
 125316        if (emptyContentMessage == null)
 0317            return;
 318
 125319        emptyContentMessage.text = newText;
 125320    }
 321
 322    public void SetLoadingActive(bool isActive)
 323    {
 3324        if (loadingPanel == null)
 0325            return;
 326
 3327        loadingPanel.SetActive(isActive);
 3328    }
 329
 330    public void SetSelectAllOptionActive(bool isActive)
 331    {
 198332        model.showSelectAllOption = isActive;
 333
 198334        if (!isMultiselect || selectAllOptionComponent == null)
 184335            return;
 336
 14337        selectAllOptionComponent.gameObject.SetActive(isActive);
 14338    }
 339
 340    public void SetOptionsPanelHeightAsDynamic(bool isDynamic, float maxHeight)
 341    {
 126342        model.isOptionsPanelHeightDynamic = isDynamic;
 126343        model.maxValueForDynamicHeight = maxHeight;
 126344        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 126345        refreshOptionsPanelCoroutine = CoroutineStarter.Start(RefreshOptionsPanelSize());
 126346    }
 347
 348    public override void Dispose()
 349    {
 197350        base.Dispose();
 351
 197352        blocker.OnClicked -= Close;
 197353        button.onClick.RemoveAllListeners();
 197354        searchBar.OnSearchText -= FilterOptions;
 355
 197356        CoroutineStarter.Stop(refreshOptionsPanelCoroutine);
 197357    }
 358
 359    internal void ToggleOptionsList()
 360    {
 2361        if (isOpen)
 1362            Close();
 363        else
 1364            Open();
 1365    }
 366
 367    internal void CreateSelectAllOption()
 368    {
 118369        CreateOption(
 370            new ToggleComponentModel
 371            {
 372                id = SELECT_ALL_OPTION_ID,
 373                text = SELECT_ALL_OPTION_TEXT,
 374                isOn = false,
 375                isTextActive = true
 376            },
 377            $"Option_{SELECT_ALL_OPTION_ID}");
 118378    }
 379
 380    internal void CreateOption(ToggleComponentModel newOptionModel, string name)
 381    {
 460382        if (togglePrefab == null)
 0383            return;
 384
 460385        ToggleComponentView newGO = Instantiate(togglePrefab);
 460386        newGO.gameObject.SetActive(true);
 460387        newGO.Configure(newOptionModel);
 460388        availableOptions.AddItemWithResize(newGO);
 460389        newGO.name = name;
 390
 460391        if (newOptionModel.id == SELECT_ALL_OPTION_ID)
 392        {
 118393            selectAllOptionComponent = newGO;
 118394            newGO.gameObject.SetActive(isMultiselect);
 395        }
 396
 460397        newGO.OnSelectedChanged += OnOptionSelected;
 460398    }
 399
 400    internal void OnOptionSelected(bool isOn, string optionId, string optionName)
 401    {
 10402        if (optionId != SELECT_ALL_OPTION_ID)
 403        {
 10404            OnOptionSelectionChanged?.Invoke(isOn, optionId, optionName);
 405
 10406            if (isOn && !isMultiselect)
 407            {
 2408                List<IToggleComponentView> allOptions = GetAllOptions();
 16409                foreach (IToggleComponentView option in allOptions)
 410                {
 6411                    if (option.id != optionId)
 4412                        option.isOn = false;
 413                }
 414            }
 415
 68416            foreach (ToggleComponentModel option in originalOptions)
 417            {
 24418                if (optionId == option.id)
 10419                    option.isOn = isOn;
 420            }
 421
 10422            UpdateSelectAllOptionStatus();
 423        }
 424        else
 425        {
 0426            SetSelectAll(isOn);
 427        }
 428
 10429        if (!isMultiselect)
 4430            Close();
 10431    }
 432
 433    internal void UpdateSelectAllOptionStatus()
 434    {
 206435        if (!isMultiselect)
 109436            return;
 437
 97438        List<IToggleComponentView> allOptions = GetAllOptions();
 439
 97440        if (selectAllOptionComponent != null)
 441        {
 18442            selectAllOptionComponent.OnSelectedChanged -= OnOptionSelected;
 39443            selectAllOptionComponent.isOn = allOptions.Count > 0 && allOptions.All(x => x.isOn);
 18444            selectAllOptionComponent.OnSelectedChanged += OnOptionSelected;
 445        }
 97446    }
 447
 448    internal void RemoveAllInstantiatedOptions()
 449    {
 198450        availableOptions.RemoveItems();
 198451    }
 452
 453    internal IEnumerator RefreshOptionsPanelSize()
 454    {
 327455        if (!model.isOptionsPanelHeightDynamic)
 157456            yield break;
 457
 170458        yield return null;
 459
 3460        Utils.ForceRebuildLayoutImmediate(availableOptionsParent);
 461
 3462        yield return null;
 463
 1464        if (optionsPanel == null)
 0465            yield break;
 466
 1467        RectTransform optionsPanelTransform = optionsPanel.transform as RectTransform;
 1468        optionsPanelTransform.sizeDelta = new Vector2(
 469            optionsPanelTransform.sizeDelta.x,
 470            Mathf.Clamp(availableOptionsParent.sizeDelta.y + BOTTOM_MARGIN_SIZE, 0f, model.maxValueForDynamicHeight));
 1471    }
 472}