Skip to content

feat(SelectCity): add SelectCity component#602

Merged
ArgoZhang merged 18 commits intomasterfrom
feat-region
Oct 15, 2025
Merged

feat(SelectCity): add SelectCity component#602
ArgoZhang merged 18 commits intomasterfrom
feat-region

Conversation

@ArgoZhang
Copy link
Copy Markdown
Member

@ArgoZhang ArgoZhang commented Oct 15, 2025

Link issues

fixes #601

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Introduce a new SelectCity component for province and city selection with optional multi-select support, and refactor the existing SelectRegion component by extracting shared logic into a SelectRegionBase class and simplifying its CSS selectors.

New Features:

  • Add SelectCity component with single and optional multiple selection modes for provinces and cities.

Enhancements:

  • Extract shared parameters, methods, and styling of SelectRegion into a new SelectRegionBase class.
  • Simplify SelectRegion component by inheriting from the new base class and removing redundant code.
  • Update CSS selectors in SelectRegion to unify dropdown styles under .dropdown-menu.

Copilot AI review requested due to automatic review settings October 15, 2025 03:57
@bb-auto bb-auto Bot added the enhancement New feature or request label Oct 15, 2025
@bb-auto bb-auto Bot added this to the v9.2.0 milestone Oct 15, 2025
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Oct 15, 2025

Reviewer's Guide

This PR refactors the existing SelectRegion component by extracting shared logic into a new base class and simplifies its CSS, then introduces a new SelectCity component that supports hierarchical single or multiple city selection.

Class diagram for SelectRegion refactor and new SelectCity component

classDiagram
    class PopoverSelectBase {
        <<abstract>>
    }
    class SelectRegionBase {
        string? PlaceHolder
        Color Color
        string? DropdownIcon
        Func<Task>? OnClearAsync
        string? ClearIcon
        IIconTheme? IconTheme
        IRegionService? RegionService
        string? InputClassString
        string? AppendClassString
        string? ClearClassString
        void OnParametersSet()
    }
    class SelectRegion {
        string? InputId
        string? ClassString
        string? GetHeaderActiveClass(RegionViewMode type)
        string? GetBodyActiveClass(RegionViewMode type)
    }
    class SelectCity {
        bool IsMultiple
        string? InputId
        string? ClassString
        HashSet<string> _values
        string? GetActiveClass(string item)
        Task OnClearValue()
        void OnSelectProvince(string province)
        void OnSelectCity(string item)
        static HashSet<string> GetProvinces()
        static HashSet<string> Municipalities
        static HashSet<string> SpecialAdministrativeRegions
        HashSet<string> GetCities(string provinceName)
    }
    PopoverSelectBase <|-- SelectRegionBase
    SelectRegionBase <|-- SelectRegion
    SelectRegionBase <|-- SelectCity
Loading

File-Level Changes

Change Details Files
Extract shared SelectRegion logic into a new base class and update SelectRegion to inherit from it
  • Moved parameters, injected services, and CSS-building methods to SelectRegionBase
  • Removed duplicated code from SelectRegion.razor.cs and updated its inheritance
  • Updated SelectRegion.razor to use SelectRegionBase
SelectRegionBase.cs
SelectRegion.razor.cs
SelectRegion.razor
Simplify SelectRegion CSS selectors
  • Replaced .popover-region scopes with .dropdown-menu nesting
  • Updated nested ul and li styles under .dropdown-menu
SelectRegion.razor.css
Add new SelectCity component for single/multiple province–city selection
  • Implemented IsMultiple flag and internal value storage
  • Added OnSelectProvince and OnSelectCity logic with support for municipalities and special regions
  • Defined static province list and city lookup via RegionService
  • Created razor markup with dynamic dropdown and clear behavior
  • Added component-specific CSS for layout and active states
SelectCity.razor.cs
SelectCity.razor
SelectCity.razor.css

Assessment against linked issues

Issue Objective Addressed Explanation
#601 Add a SelectCity component to the codebase.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a new SelectCity component for BootstrapBlazor.Region that provides functionality to select cities by province. The component refactors existing code by extracting common functionality into a base class and creates a specialized city selector with province grouping.

  • Created SelectRegionBase abstract class to share common functionality between region selection components
  • Added SelectCity component with support for single and multiple city selection
  • Refactored SelectRegion component to inherit from the new base class

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
SelectRegionBase.cs New abstract base class containing shared properties and methods for region selection components
SelectRegion.razor.cs Refactored to inherit from SelectRegionBase, removing duplicated code
SelectRegion.razor Updated to inherit from SelectRegionBase instead of PopoverSelectBase
SelectRegion.razor.css Simplified CSS selectors by removing .popover-region specificity
SelectCity.razor New component template for city selection with province grouping
SelectCity.razor.cs Implementation of city selection logic with single/multiple selection support
SelectCity.razor.css Styling for the city selection dropdown with flex layout

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread src/components/BootstrapBlazor.Region/Components/SelectCity.razor.cs Outdated
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • Consider extracting the hard-coded province and city lists into a dedicated service or resource file (and potentially localize them) for better maintainability.
  • To prevent global CSS collisions, scope the new dropdown styles under the .bb-city class (e.g. .bb-city .dropdown-menu) instead of using the generic .dropdown-menu selector.
  • For multiple selection, using a comma-delimited string in CurrentValue can be error-prone—expose a strongly-typed collection (e.g. IList<string>) to simplify binding and avoid manual parsing.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider extracting the hard-coded province and city lists into a dedicated service or resource file (and potentially localize them) for better maintainability.
- To prevent global CSS collisions, scope the new dropdown styles under the `.bb-city` class (e.g. `.bb-city .dropdown-menu`) instead of using the generic `.dropdown-menu` selector.
- For multiple selection, using a comma-delimited string in `CurrentValue` can be error-prone—expose a strongly-typed collection (e.g. `IList<string>`) to simplify binding and avoid manual parsing.

## Individual Comments

### Comment 1
<location> `src/components/BootstrapBlazor.Region/Components/SelectCity.razor.cs:43-52` </location>
<code_context>
+        }
+    }
+
+    private void OnSelectProvince(string province)
+    {
+        if (IsMultiple)
+        {
+            HashSet<string> cities = [];
+            if (province == "直辖市")
+            {
+                cities = Municipalities;
+            }
+            else if (province == "特别行政区")
+            {
+                cities = SpecialAdministrativeRegions;
+            }
+            else
+            {
+                cities = GetCities(province);
+            }
+            foreach (var city in cities)
+            {
+                if (!_values.Remove(city))
</code_context>

<issue_to_address>
**question:** The toggling logic for city selection may lead to unintuitive behavior for users.

Currently, clicking a province toggles each city's selection, which could result in all cities being deselected if they were already selected. Please clarify whether province selection should consistently select or deselect all cities, rather than toggling.
</issue_to_address>

### Comment 2
<location> `src/components/BootstrapBlazor.Region/Components/SelectCity.razor.cs:128-137` </location>
<code_context>
+
+    private static readonly HashSet<string> SpecialAdministrativeRegions = ["香港特别行政区", "澳门特别行政区"];
+
+    private HashSet<string> GetCities(string provinceName)
+    {
+        if (provinceName == "直辖市")
+        {
+            return Municipalities;
+        }
+
+        if (provinceName == "特别行政区")
+        {
+            return SpecialAdministrativeRegions;
+        }
+
+        return RegionService.GetCities(provinceName);
+    }
+}
</code_context>

<issue_to_address>
**issue (bug_risk):** Potential null reference if RegionService is not set.

RegionService may be null, leading to a NullReferenceException. Please add a null check or provide a fallback.
</issue_to_address>

### Comment 3
<location> `src/components/BootstrapBlazor.Region/Components/SelectCity.razor:14-17` </location>
<code_context>
+    </div>
+    @if (!IsDisabled)
+    {
+        <span class="@ClearClassString" @onclick="OnClearValue"><i class="@ClearIcon"></i></span>
+    }
+    <div class="dropdown-menu">
</code_context>

<issue_to_address>
**suggestion:** Clear button is always rendered when not disabled, even if no value is present.

Consider rendering the clear button only when a value is present to avoid user confusion.

```suggestion
    @if (!IsDisabled && !string.IsNullOrEmpty(CurrentValueAsString))
    {
        <span class="@ClearClassString" @onclick="OnClearValue"><i class="@ClearIcon"></i></span>
    }
```
</issue_to_address>

### Comment 4
<location> `src/components/BootstrapBlazor.Region/Components/SelectCity.razor.cs:87` </location>
<code_context>
+        }
+    }
+
+    private static HashSet<string> GetProvinces()
+    {
+        return
</code_context>

<issue_to_address>
**issue (complexity):** Consider moving hard-coded province and city data into a JSON file and a service to simplify lookups and reduce branching.

Here’s a way to collapse all of that hard-coded data and branching into a simple lookup. Move your province/city lists into a JSON or a `RegionService`, then in your component just call into that service.  

1) Create a JSON file (e.g. regions.json):
```json
{
  "直辖市":    ["北京市","天津市","上海市","重庆市"],
  "特别行政区":["香港特别行政区","澳门特别行政区"],
  "河北省":    [ /* …河北所有市… */ ],
  "山西省":    [ /* … */ ],
  // …etc…
}
```

2) Load it in a singleton service:
```csharp
public class RegionService
{
  private readonly IReadOnlyDictionary<string, IReadOnlyList<string>> _map;

  public RegionService(IConfiguration config)
  {
    _map = config
      .GetSection("Regions")
      .Get<Dictionary<string, List<string>>>()
      ?? throw new InvalidOperationException("Regions missing");
  }

  public IEnumerable<string> GetProvinces() => _map.Keys;
  public IEnumerable<string> GetCities(string province) 
    => _map.TryGetValue(province, out var cities)
      ? cities
      : Array.Empty<string>();
}
```

3) Inject and simplify your component:
```csharp
@inject RegionService RegionService

//
private IEnumerable<string> Provinces => RegionService.GetProvinces();

private void OnSelectProvince(string province)
{
  var cities = RegionService.GetCities(province);
  foreach (var city in cities)
    ToggleValue(city);
  CurrentValue = string.Join(",", _values);
}

private void OnSelectCity(string item)
  => IsMultiple ? (ToggleValue(item), CurrentValue = string.Join(",", _values))
                : CurrentValue = item;
```

Notice there’s no more special‐case branches or in‐file HashSets. All data lives in one JSON, and your component just calls a simple lookup table.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/components/BootstrapBlazor.Region/Components/SelectCity.razor
@ArgoZhang ArgoZhang merged commit 086e832 into master Oct 15, 2025
2 checks passed
@ArgoZhang ArgoZhang deleted the feat-region branch October 15, 2025 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(SelectCity): add SelectCity component

2 participants