
* web: fix Flash of Unstructured Content while SearchSelect is loading from the backend Provide an alternative, readonly, disabled, unindexed input object with the text "Loading...", to be replaced with the _real_ input element after the content is loaded. This provides the correct appearance and spacing so the content doesn't jiggle about between the start of loading and the SearchSelect element being finalized. It was visually distracting and unappealing. * web: comment on state management in API layer, move file to point to correct component under test. * web: test for flash of unstructured content - Add a unit test to ensure the "Loading..." element is displayed correctly before data arrives - Demo how to mock a `fetchObjects()` call in testing. Very cool. - Make distinguishing rule sets for code, tests, and scripts in nightmare mode - In SearchSelect, Move the `styles()` declaration to the top of the class for consistency. - To test for the FLOUC issue in SearchSelect. This is both an exercise in mocking @beryju's `fetchObjects()` protocol, and shows how we can unit test generic components that render API objects.
73 lines
2.6 KiB
TypeScript
73 lines
2.6 KiB
TypeScript
import { TemplateResult } from "lit";
|
|
import { customElement, property } from "lit/decorators.js";
|
|
|
|
import { type ISearchSelectBase, SearchSelectBase } from "./SearchSelect.js";
|
|
|
|
export interface ISearchSelectApi<T> {
|
|
fetchObjects: (query?: string) => Promise<T[]>;
|
|
renderElement: (element: T) => string;
|
|
renderDescription?: (element: T) => string | TemplateResult;
|
|
value: (element: T | undefined) => unknown;
|
|
selected?: (element: T, elements: T[]) => boolean;
|
|
groupBy: (items: T[]) => [string, T[]][];
|
|
}
|
|
|
|
export interface ISearchSelectEz<T> extends ISearchSelectBase<T> {
|
|
config: ISearchSelectApi<T>;
|
|
}
|
|
|
|
/**
|
|
* @class SearchSelectEz
|
|
* @element ak-search-select-ez
|
|
*
|
|
* The API layer of ak-search-select, now in EZ format!
|
|
*
|
|
* - @prop config (Object): A Record <string, function> that fulfills the API needed by Search
|
|
* Select to retrieve, filter, group, describe, and return elements.
|
|
* - @attr blankable (boolean): if true, the component is blankable and can return `undefined`
|
|
* - @attr name (string): The name of the component, for forms
|
|
* - @attr query (string): The current search criteria for fetching objects
|
|
* - @attr placeholder (string): What to show when the input is empty
|
|
* - @attr emptyOption (string): What to show in the menu to indicate "leave this undefined". Only
|
|
* shown if `blankable`
|
|
* - @attr selectedObject (Object<T>): The current object, or undefined, selected
|
|
*
|
|
* ¹ Due to a limitation in the parsing of properties-vs-attributes, these must be defined as
|
|
* properties, not attributes. As a consequence, they must be declared in property syntax.
|
|
* Example:
|
|
*
|
|
* `.renderElement=${"name"}`
|
|
*
|
|
* - @fires ak-change - When a value from the collection has been positively chosen, either as a
|
|
* consequence of the user typing or when selecting from the list.
|
|
*
|
|
*/
|
|
|
|
@customElement("ak-search-select-ez")
|
|
export class SearchSelectEz<T> extends SearchSelectBase<T> implements ISearchSelectEz<T> {
|
|
static get styles() {
|
|
return [...SearchSelectBase.styles];
|
|
}
|
|
|
|
@property({ type: Object, attribute: false })
|
|
config!: ISearchSelectApi<T>;
|
|
|
|
connectedCallback() {
|
|
this.fetchObjects = this.config.fetchObjects;
|
|
this.renderElement = this.config.renderElement;
|
|
this.renderDescription = this.config.renderDescription;
|
|
this.value = this.config.value;
|
|
this.selected = this.config.selected;
|
|
this.groupBy = this.config.groupBy;
|
|
super.connectedCallback();
|
|
}
|
|
}
|
|
|
|
export default SearchSelectEz;
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"ak-search-select-ez": SearchSelectEz<unknown>;
|
|
}
|
|
}
|