Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HLD] Select custom filtering. #2125

Merged
merged 19 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3d2a8d0
Update spec with new APIs for Select custom filtering.
atmgrifter00 May 20, 2024
9af432b
Change files
atmgrifter00 May 20, 2024
0b23912
Merge branch 'main' into select-dynamic-options-HLD
atmgrifter00 May 20, 2024
44f608c
Update packages/nimble-components/src/select/specs/filterable-select-…
atmgrifter00 May 21, 2024
1a1dedf
Update packages/nimble-components/src/select/specs/filterable-select-…
atmgrifter00 May 21, 2024
7ee8b6e
Update packages/nimble-components/src/select/specs/filterable-select-…
atmgrifter00 May 21, 2024
f5ad04e
Handle PR feedback.
atmgrifter00 May 22, 2024
9279081
Merge branch 'select-dynamic-options-HLD' of https://github.com/ni/ni…
atmgrifter00 May 22, 2024
7ffc362
Handle PR feedback.
atmgrifter00 May 30, 2024
6f80429
Minor update.
atmgrifter00 May 30, 2024
deebe34
Update packages/nimble-components/src/select/specs/filterable-select-…
atmgrifter00 May 31, 2024
81e9c0a
Handle PR feedback.
atmgrifter00 May 31, 2024
c0f5f5b
Merge branch 'select-dynamic-options-HLD' of https://github.com/ni/ni…
atmgrifter00 May 31, 2024
ef9438c
Merge branch 'main' into select-dynamic-options-HLD
atmgrifter00 May 31, 2024
b033a2e
Update packages/nimble-components/src/select/specs/filterable-select-…
atmgrifter00 May 31, 2024
00054d1
Handle PR feedback.
atmgrifter00 May 31, 2024
45ba80b
Merge branch 'main' into select-dynamic-options-HLD
atmgrifter00 May 31, 2024
76cdb3e
Merge branch 'main' into select-dynamic-options-HLD
atmgrifter00 May 31, 2024
a7ff979
Merge branch 'main' into select-dynamic-options-HLD
atmgrifter00 May 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "Update spec with new APIs for Select custom filtering.",
"packageName": "@ni/nimble-components",
"email": "26874831+atmgrifter00@users.noreply.github.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,58 @@ Our clients have a need of a filterable dropdown component that does not allow a

#### Select

We will provide a means for clients to enable this feature with a new attribute:
We will provide a means for clients to enable this feature with some new attributes:

```ts
export class Select() {
...
@attr({ attribute: 'filter-mode' })
public filterMode = FilterMode.none;

/**
* Displays a message at the bottom of the dropdown to indicate more options
* are currently being loaded.
*/
@attr({ attribute: 'is-loading' })
public isLoading = false;
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
...
}

// Provide enum for filterMode to allow for future modes
export const FilterMode = {
none: undefined;
standard: 'standard';
custom: 'custom';
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
} as const;
```

- The `standard` filterMode will result in case-insensitive, diacritic-insensitive filtering.
- `filterMode` will default to `none` so as not to affect existing clients.
- The `custom` filterMode will result in a search input being shown, but no automatic filtering will occur as the user types in the search box. Instead, it is expected that a client leverage the `filter-input`event described in the ['Dynamic Options'](#dynamic-options) section below.
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
- The `none` filter mode results in no search input being shown in the dropdown.
- `filterMode` will default to `none` so as not to affect existing clients.

_Note: The `filterMode` isn't meant to mirror the `Combobox` `autocomplete` API, as they do serve slightly different purposes: The `autocomplete` for the `Combobox` ultimately helps set the actual value of the `Combobox` as the user types, and isn't necessarily performing any filtering (e.g. the `inline` mode). One possible concern, however, is that we are presenting an API that will allow different types of filter behaviors (i.e. case sensitive) that the `Combobox` does not support. Additionally, I am proposing diacritic insensitive filtering, which the `Combobox` also does not currently support, but I feel this is quite likely a better default experience._

#### Dynamic options

In order to support the use-case of clients supplying options to the `Select` based on the current search text, clients will need to set the `filterMode` to `custom`, and then subscribe to the `filter-input` event, the details of which will provide the current search text:
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved

```ts
interface SelectFilterInputEventDetail {
filterText: string;
}
```

Then, in the handling of the `filter-input` event, clients will need to perform the desired filtering using the supplied filter text, in addition to setting the `is-loading` attribute to `true` while the options are being loaded into the DOM, and then again to `false` when that process is complete.
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved

_IMPORTANT_: When using the `custom` filter mode, clients are responsible for making sure the options that are placed in the DOM meet one of the following criteria:
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved

- The option is some reasonable default option when there is no filter text provided _OR_
- The option display text matches the provided filter in an understandable way _OR_
- The option is a placeholder option

_Note: When the `isLoading` attribute is set, we will display localizable text at the bottom of the dropdown (defaulting to "Loading"), along with the spinner icon._

#### LabelProviderCore

We will be showing text that will require localization as part of this feature. It seems reasonable to add these APIs to the existing `LabelProviderCore` class as opposed to creating a new provider:
Expand All @@ -55,12 +84,16 @@ export class LabelProviderCore

@attr({ attribute: 'select-filter-no-results' })
public selectFilterNoResults: string | undefined;

@attr({ attribute: 'select-is-loading' })
public selectIsLoading: string | undefined;
```

The English strings used for these labels will be:

- selectFilterSearch: "Search"
- selectFilterNoResults: "No items found"
- selectIsLoading: "Loading"

### Implementation details

Expand Down Expand Up @@ -88,6 +121,8 @@ The accessibility tree will report that the search `input` element should have i

### Future considerations

It's possible that we may want to improve/alter the discoverability of the fact that more options are available to be loaded when using the `custom` filtering option. This _could_ follow the design of what we see with the AzDO _Windows_-based user selector experiece where a display of the number of current results is shown at the bottom, and when a user begins typing in the filter input, a 'Show more results' button is shown at the bottom of the dropdown.

#### Grouping/Metadata

One feature that we intend to add to the `Select` is the ability to specify "groups" of options, where each group will have non-selectable header text followed by the options under that group. Ultimately, this feature will have to work nicely with filtering, but I don't believe there are aspects of this that would interfere with the current proposed API in this HLD of a single attribute that specifies how the filter text is applied to a target.
Expand All @@ -98,18 +133,10 @@ There is also a desire to allow the [`ListOption` to contain complex content](ht

It may be desireable to have other filter modes in the future, such as case sensitive, or even regular expressions. By making the new API an enum, we can easily add new modes as needed.

#### Dynamic fetching of options

We know that there is a use-case with the `Combobox` to dynamically fetch options from a server that match the pattern provided in the input field, and so it isn't a stretch that a client might want the same capability in the `Select`. However, this is currently accomplished through turning off the `Combobox` `autocomplete` mode, and essentially having the client provide a custom behavior.

The `Select` presents its own challenges for providing a similar ability:

- Should the `Combobox` and `Select` provide mirrored APIs for this? Currently, this doesn't seem possible in Angular as the `Combobox` relies on users accessing its `value` property from the `nativeElement` to use for the filter, in combination with listening to native `input` events. The `Select` would either need to work differently, or the `Combobox` would have to be updated.
- Would this feature be enabled through another mode on the `filterMode` enum (i.e. a `dynamic` or `custom` mode), or is it orthogonal to the `filterMode` API?
- Are there challenges in having the filter work against local options in addition to retrieving new ones?

## Alternative Implementations / Designs

None

## Open Issues

- Does the `Combobox` need to present a mirrored API to the `Select` for dynmically loaded options (i.e. an `isLoading` attribute along with the accompanying visuals, and a `filter-input` event)
fredvisser marked this conversation as resolved.
Show resolved Hide resolved
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
Loading