diff --git a/packages/host/app/components/card-catalog/modal.gts b/packages/host/app/components/card-catalog/modal.gts index 5280796085..93a988b97a 100644 --- a/packages/host/app/components/card-catalog/modal.gts +++ b/packages/host/app/components/card-catalog/modal.gts @@ -24,6 +24,7 @@ import { type CreateNewCard, Deferred, type RealmInfo, + Loader, } from '@cardstack/runtime-common'; import type { Query, Filter } from '@cardstack/runtime-common/query'; @@ -327,7 +328,11 @@ export default class CardCatalogModal extends Component { } = {}, ) => { this.stateId++; - let title = chooseCardTitle(query.filter, opts?.multiSelect); + let title = await chooseCardTitle( + query.filter, + this.loaderService.loader, + opts?.multiSelect, + ); let request = new TrackedObject({ search: getSearchResults(this, () => query), deferred: new Deferred(), @@ -527,14 +532,18 @@ export default class CardCatalogModal extends Component { ); } -function chooseCardTitle( +async function chooseCardTitle( filter: Filter | undefined, + loader: Loader, multiSelect?: boolean, -): string { +): Promise { if (!filter) { return DEFAULT_CHOOOSE_CARD_TITLE; } - let suggestions = suggestCardChooserTitle(filter, 0, { multiSelect }); + let suggestions = await suggestCardChooserTitle(filter, 0, { + loader, + multiSelect, + }); return ( getSuggestionWithLowestDepth(suggestions) ?? DEFAULT_CHOOOSE_CARD_TITLE ); diff --git a/packages/host/app/utils/text-suggestion.ts b/packages/host/app/utils/text-suggestion.ts index 3f1970e0dd..80081f5ed2 100644 --- a/packages/host/app/utils/text-suggestion.ts +++ b/packages/host/app/utils/text-suggestion.ts @@ -1,6 +1,11 @@ import a from 'indefinite'; -import { getPlural } from '@cardstack/runtime-common'; +import { + CodeRef, + getPlural, + loadCard, + Loader, +} from '@cardstack/runtime-common'; import { isCardTypeFilter, isEveryFilter, @@ -12,14 +17,15 @@ interface ChooseCardSuggestion { depth: number; } -interface TextOpts { +interface Opts { + loader: Loader; multiSelect?: boolean; } -export function suggestCardChooserTitle( +export async function suggestCardChooserTitle( filter: Filter, depth = 0, //lower the depth, higher the priority - textOpts?: TextOpts, -): ChooseCardSuggestion[] { + opts: Opts, +): Promise { let MAX_RECURSION_DEPTH = 2; if (filter === undefined || depth + 1 > MAX_RECURSION_DEPTH) { return []; @@ -27,28 +33,30 @@ export function suggestCardChooserTitle( let suggestions: ChooseCardSuggestion[] = []; //--base case-- if ('on' in filter && filter.on !== undefined) { - let cardRefName = (filter.on as { module: string; name: string }).name; - return [{ suggestion: titleText(cardRefName, 'card', textOpts), depth }]; + let cardDisplayName = await getCardDisplayName(opts.loader, filter.on); + return [{ suggestion: titleText(cardDisplayName, 'card', opts), depth }]; } if (isCardTypeFilter(filter)) { - let cardRefName = (filter.type as { module: string; name: string }).name; - if (cardRefName == 'CardDef') { + let cardDisplayName = await getCardDisplayName(opts.loader, filter.type); + if (cardDisplayName == 'Card') { suggestions.push({ - suggestion: titleText('Card', 'instance', textOpts), + suggestion: titleText('Card', 'instance', opts), depth, }); } else { suggestions.push({ - suggestion: titleText(cardRefName, 'card', textOpts), + suggestion: titleText(cardDisplayName, 'card', opts), depth, }); } } //--inductive case-- if (isEveryFilter(filter)) { - let nestedSuggestions = filter.every.flatMap((f) => - suggestCardChooserTitle(f, depth + 1, textOpts), - ); + let nestedSuggestions = await Promise.all( + filter.every.map( + async (f) => await suggestCardChooserTitle(f, depth + 1, opts), + ), + ).then((arrays) => arrays.flat()); suggestions = [...suggestions, ...nestedSuggestions]; } return suggestions; @@ -56,19 +64,20 @@ export function suggestCardChooserTitle( type CardNoun = 'instance' | 'type' | 'card'; -function titleText( - cardRefName: string, - cardNoun: CardNoun, - textOpts?: TextOpts, -) { - let object = `${cardRefName} ${cardNoun}`; - if (textOpts?.multiSelect) { +function titleText(cardDisplayName: string, cardNoun: CardNoun, opts?: Opts) { + let object = `${cardDisplayName} ${cardNoun}`; + if (opts?.multiSelect) { return `Select 1 or more ${getPlural(object)}`; } else { return `Choose ${a(object)}`; } } +async function getCardDisplayName(loader: Loader, codeRef: CodeRef) { + let card = await loadCard(codeRef, { loader }); + return card.displayName; +} + export function getSuggestionWithLowestDepth( items: ChooseCardSuggestion[], ): string | undefined { diff --git a/packages/host/tests/integration/components/card-editor-test.gts b/packages/host/tests/integration/components/card-editor-test.gts index a93b01adfc..d2bca5676d 100644 --- a/packages/host/tests/integration/components/card-editor-test.gts +++ b/packages/host/tests/integration/components/card-editor-test.gts @@ -84,6 +84,7 @@ module('Integration | card-editor', function (hooks) { let { default: StringField } = string; class Pet extends CardDef { + static displayName = 'Pet'; @field name = contains(StringField); static embedded = class Embedded extends Component {