Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -32,7 +32,10 @@ import {
type ResolvedCodeRef,
} from '@cardstack/runtime-common';

import { CardContextName } from '@cardstack/runtime-common';
import {
CardContextName,
CardCrudFunctionsContextName,
} from '@cardstack/runtime-common';

import CardRenderer from '@cardstack/host/components/card-renderer';
import Overlays from '@cardstack/host/components/operator-mode/overlays';
Expand All @@ -54,6 +57,7 @@ import MetadataPanel from './metadata-panel';
import type {
BaseDef,
CardContext,
CardCrudFunctions,
CardDef,
Format,
ViewCardFn,
Expand Down Expand Up @@ -211,6 +215,26 @@ export default class PreviewPanel extends Component<Signature> {
};
}

/**
* Card templates receive their crud functions through this context rather
* than through args (see `field-component.gts`), and the two providers of
* it are the interact-mode stack and host mode. Without one here, a button
* inside a previewed card that calls `viewCard` — the Workspace card's
* pinned tiles, a Library tile, any card that navigates — silently does
* nothing, because `viewCard` is undefined and every call site guards with
* `?.`. The pane's own `@viewCard` only ever reached the hover overlays.
*
* Only `viewCard` is provided: it navigates the code path, which is what
* the overlay buttons in this same pane already do. The rest stay
* undefined, so an in-card create/edit/delete button remains inert here
* instead of doing something a code-mode preview cannot honor.
*/
@provide(CardCrudFunctionsContextName)
// @ts-ignore context is used via provider
private get cardCrudFunctions(): Partial<CardCrudFunctions> {
return { viewCard: this.args.viewCard };
Comment on lines +232 to +235

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Provide navigation to playground previews too

When previewing an instance selected from a .gts card definition, module-inspector.gts renders Playground (lines 653–660), whose playground-preview.gts renders CardRenderer directly rather than through PreviewPanel. Consequently, this provider is not an ancestor of that card template, so controls using viewCard remain inert in the module playground; the provider needs to cover that preview path as well, with a focused test for navigation from a definition's selected instance.

Useful? React with 👍 / 👎.

}

private get renderedCardsForOverlayActions():
| RenderedCardForOverlayActions[]
| undefined {
Expand Down
73 changes: 73 additions & 0 deletions packages/host/tests/acceptance/code-submode/inspector-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,39 @@ const friendCardSource = `
}
`;

// A card whose template navigates through `viewCard`, the way the Workspace
// card's pinned tiles and Library tiles do. Card templates receive that
// function from the card-crud-functions context, so this fixture is what
// proves the code-mode preview pane provides it.
const navigatorCardSource = `
import { contains, field, linksTo, CardDef, Component } from "@cardstack/base/card-api";
import { on } from '@ember/modifier';
import StringField from "@cardstack/base/string";

export class Navigator extends CardDef {
static displayName = 'Navigator';
@field name = contains(StringField);
@field target = linksTo(() => Navigator);
static isolated = class Isolated extends Component<typeof this> {
openTarget = () => {
let { target } = this.args.model;
if (target) {
this.args.viewCard?.(target);
}
};
<template>
<div data-test-navigator>
<button
type='button'
data-test-open-target
{{on 'click' this.openTarget}}
>Open target</button>
</div>
</template>
};
}
`;

const exportsSource = `
import {
contains,
Expand Down Expand Up @@ -519,6 +552,26 @@ module('Acceptance | code submode | inspector tests', function (hooks) {
'person.gts': personCardSource,
'pet.gts': petCardSource,
'friend.gts': friendCardSource,
'navigator.gts': navigatorCardSource,
'Navigator/parent.json': {
data: {
attributes: { name: 'Parent' },
relationships: {
target: { links: { self: './child' } },
},
meta: {
adoptsFrom: { module: '../navigator', name: 'Navigator' },
},
},
},
'Navigator/child.json': {
data: {
attributes: { name: 'Child' },
meta: {
adoptsFrom: { module: '../navigator', name: 'Navigator' },
},
},
},
'employee.gts': employeeCardSource,
'in-this-file.gts': inThisFileSource,
'exports.gts': exportsSource,
Expand Down Expand Up @@ -993,6 +1046,26 @@ module('Acceptance | code submode | inspector tests', function (hooks) {
assert.dom('[data-test-card-url-bar-input]').hasValue(`${id}.json`);
});

test('a card in the preview pane can navigate through viewCard', async function (assert) {
await visitOperatorMode({
submode: 'code',
codePath: `${testRealmURL}Navigator/parent.json`,
});

await waitFor('[data-test-open-target]');
await click('[data-test-open-target]');

await waitFor(
`[data-test-code-mode-card-renderer-header="${testRealmURL}Navigator/child"]`,
);
assert
.dom('[data-test-card-url-bar-input]')
.hasValue(
`${testRealmURL}Navigator/child.json`,
'the in-card button opened the linked card, like the hover overlays do',
);
});

test<TestContextWithSave>('can duplicate an instance in different realm', async function (assert) {
assert.expect(8);
let operatorModeStateParam = stringify({
Expand Down
Loading