@lotics/ui 7.19.2 → 7.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -190,4 +190,43 @@ describe("useOptionList", () => {
|
|
|
190
190
|
});
|
|
191
191
|
expect(view.result.current.rows.map((r) => r.option.value)).toEqual(["z"]);
|
|
192
192
|
});
|
|
193
|
+
|
|
194
|
+
it("controlled server-filtered mode browses recents on an empty query", () => {
|
|
195
|
+
const recentOptions = [
|
|
196
|
+
{ value: "r1", label: "Recent one" },
|
|
197
|
+
{ value: "r2", label: "Recent two" },
|
|
198
|
+
];
|
|
199
|
+
// A server search has nothing for an empty term — recents are the idle list.
|
|
200
|
+
const params: UseOptionListParams<string, false> = {
|
|
201
|
+
options: [],
|
|
202
|
+
search: { mode: "controlled", query: "", serverFiltered: true },
|
|
203
|
+
recentOptions,
|
|
204
|
+
};
|
|
205
|
+
const view = renderHook((p: UseOptionListParams<string, false>) => useOptionList(p), {
|
|
206
|
+
initialProps: params,
|
|
207
|
+
});
|
|
208
|
+
expect(view.result.current.searching).toBe(false);
|
|
209
|
+
expect(view.result.current.rows.map((r) => r.option.value)).toEqual(["r1", "r2"]);
|
|
210
|
+
// Typing hands the list back to the server's results.
|
|
211
|
+
view.rerender({
|
|
212
|
+
...params,
|
|
213
|
+
options: [{ value: "z", label: "Zucchini" }],
|
|
214
|
+
search: { mode: "controlled", query: "zu", serverFiltered: true },
|
|
215
|
+
});
|
|
216
|
+
expect(view.result.current.rows.map((r) => r.option.value)).toEqual(["z"]);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("an empty controlled query without recents browses the full option set", () => {
|
|
220
|
+
// The `recentOptions ?? options` idle contract — a consumer that supplies
|
|
221
|
+
// browse options (server or local) and no recents keeps its full list.
|
|
222
|
+
for (const serverFiltered of [true, false]) {
|
|
223
|
+
const view = renderHook((p: UseOptionListParams<string, false>) => useOptionList(p), {
|
|
224
|
+
initialProps: {
|
|
225
|
+
options: OPTS,
|
|
226
|
+
search: { mode: "controlled" as const, query: "", serverFiltered },
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
expect(view.result.current.rows.map((r) => r.option.value)).toEqual(["a", "b", "c"]);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
193
232
|
});
|
package/src/use_option_list.ts
CHANGED
|
@@ -172,12 +172,15 @@ export function useOptionList<T extends string, MULTI extends boolean = false, D
|
|
|
172
172
|
[options],
|
|
173
173
|
);
|
|
174
174
|
const filtered = useMemo(() => {
|
|
175
|
-
if (searchMode === "none"
|
|
175
|
+
if (searchMode === "none") return cleanOptions;
|
|
176
176
|
if (!searching) {
|
|
177
177
|
// Empty query: a controlled (Combobox) field browses recents, else its full
|
|
178
|
-
// local set; an internal field shows everything.
|
|
178
|
+
// local set; an internal field shows everything. This must precede the
|
|
179
|
+
// server-filtered short-circuit — a server search returns nothing for an
|
|
180
|
+
// empty term, so recents are the only idle content it has.
|
|
179
181
|
return searchMode === "controlled" ? (recentOptions ?? cleanOptions) : cleanOptions;
|
|
180
182
|
}
|
|
183
|
+
if (serverFiltered) return cleanOptions;
|
|
181
184
|
const q = normalizeForSearch(query);
|
|
182
185
|
return cleanOptions.filter((o) => normalizeForSearch(o.label ?? o.value).includes(q));
|
|
183
186
|
}, [searchMode, serverFiltered, searching, cleanOptions, recentOptions, query]);
|