diff --git a/packages/base/contains-many-component.gts b/packages/base/contains-many-component.gts
index 81c7b5408c..91e442efa1 100644
--- a/packages/base/contains-many-component.gts
+++ b/packages/base/contains-many-component.gts
@@ -361,23 +361,36 @@ export function getContainsManyComponent({
(coalesce @format defaultFormats.fieldDef)
as |effectiveFormat|
}}
-
+ {{/if}}
{{/let}}
{{/if}}
diff --git a/packages/base/field-component.gts b/packages/base/field-component.gts
index feb40ded31..37f5b120ac 100644
--- a/packages/base/field-component.gts
+++ b/packages/base/field-component.gts
@@ -29,6 +29,7 @@ import {
import type { ComponentLike } from '@glint/template';
import { CardContainer } from '@cardstack/boxel-ui/components';
import {
+ coalesce,
extractCssVariables,
sanitizeHtmlSafe,
} from '@cardstack/boxel-ui/helpers';
@@ -371,7 +372,12 @@ export function getBoxComponent(
{{/let}}
- {{else if (isCompoundField model.value)}}
+ {{else if
+ (and
+ (isCompoundField model.value)
+ (coalesce @displayContainer true)
+ )
+ }}
diff --git a/packages/experiments-realm/Family/2a48e225-71c2-445d-affc-76fcf4f8c21c.json b/packages/experiments-realm/Family/2a48e225-71c2-445d-affc-76fcf4f8c21c.json
new file mode 100644
index 0000000000..c8a46e5b79
--- /dev/null
+++ b/packages/experiments-realm/Family/2a48e225-71c2-445d-affc-76fcf4f8c21c.json
@@ -0,0 +1,41 @@
+{
+ "data": {
+ "meta": {
+ "adoptsFrom": {
+ "name": "Family",
+ "module": "../family"
+ }
+ },
+ "type": "card",
+ "attributes": {
+ "people": [
+ {
+ "firstName": "Mango",
+ "lastName": "A."
+ },
+ {
+ "firstName": "Marco",
+ "lastName": "N."
+ }
+ ],
+ "cardInfo": {
+ "name": null,
+ "notes": null,
+ "summary": null,
+ "cardThumbnailURL": null
+ }
+ },
+ "relationships": {
+ "cardInfo.theme": {
+ "links": {
+ "self": null
+ }
+ },
+ "cardInfo.cardThumbnail": {
+ "links": {
+ "self": null
+ }
+ }
+ }
+ }
+}
diff --git a/packages/experiments-realm/family.gts b/packages/experiments-realm/family.gts
new file mode 100644
index 0000000000..4a9542d2cb
--- /dev/null
+++ b/packages/experiments-realm/family.gts
@@ -0,0 +1,56 @@
+import {
+ CardDef,
+ FieldDef,
+ Component,
+ field,
+ contains,
+ containsMany,
+ StringField,
+} from 'https://cardstack.com/base/card-api';
+import { BoxelContainer } from '@cardstack/boxel-ui/components';
+
+class FamilyMember extends FieldDef {
+ static displayName = 'Family Member';
+ @field firstName = contains(StringField);
+ @field lastName = contains(StringField);
+ static embedded = class Embedded extends Component {
+
+ <@fields.firstName />
+ <@fields.lastName />
+
+ };
+ static fitted = this.embedded;
+}
+
+export class Family extends CardDef {
+ static displayName = 'Family';
+ @field people = containsMany(FamilyMember);
+ static isolated = class Isolated extends Component {
+
+
+ Family (no chrome `containsMany`):
+
+ <@fields.people @displayContainer={{false}} />
+
+
+
+ Family (default `containsMany`):
+
+ <@fields.people />
+
+
+
+
+ Family List
+
+ {{#each @fields.people as |FamilyMember|}}
+ -
+
+
+ {{/each}}
+
+
+
+
+ };
+}
diff --git a/packages/host/app/components/operator-mode/code-submode/playground/playground-preview.gts b/packages/host/app/components/operator-mode/code-submode/playground/playground-preview.gts
index 814e4c8005..c5eb528405 100644
--- a/packages/host/app/components/operator-mode/code-submode/playground/playground-preview.gts
+++ b/packages/host/app/components/operator-mode/code-submode/playground/playground-preview.gts
@@ -91,7 +91,7 @@ const PlaygroundPreview: TemplateOnlyComponent =
class='atom-preview'
@card={{@card}}
@format={{@format}}
- @displayContainer={{false}}
+ @displayContainer={{unless @isFieldDef false}}
/>
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
diff --git a/packages/host/tests/integration/components/card-basics-test.gts b/packages/host/tests/integration/components/card-basics-test.gts
index 128855122a..3aa33cc528 100644
--- a/packages/host/tests/integration/components/card-basics-test.gts
+++ b/packages/host/tests/integration/components/card-basics-test.gts
@@ -2991,6 +2991,101 @@ module('Integration | card-basics', function (hooks) {
.hasStyle({ height: '32px' });
});
+ test('renders containsMany composite field without chrome when displayContainer is false', async function (this: RenderingTestContext, assert) {
+ class Person extends FieldDef {
+ @field firstName = contains(StringField);
+ @field lastName = contains(StringField);
+ static embedded = class Embedded extends Component {
+
+ <@fields.firstName />
+ <@fields.lastName />
+
+ };
+ }
+
+ class Family extends CardDef {
+ @field people = containsMany(Person);
+ static isolated = class Isolated extends Component {
+
+
+ <@fields.people @displayContainer={{false}} />
+
+
+ };
+ }
+ loader.shimModule(`${testRealmURL}test-cards`, { Family, Person });
+
+ let abdelRahmans = new Family({
+ people: [
+ new Person({ firstName: 'Mango', lastName: 'Abdel-Rahman' }),
+ new Person({ firstName: 'Van Gogh', lastName: 'Abdel-Rahman' }),
+ ],
+ });
+
+ await renderCard(loader, abdelRahmans, 'isolated');
+
+ assert
+ .dom('.plural-field')
+ .doesNotExist('containsMany wrapper div is not rendered');
+ assert
+ .dom('.containsMany-item')
+ .doesNotExist('per-item wrapper divs are not rendered');
+ assert
+ .dom('[data-test-compound-field-component]')
+ .doesNotExist('compound field wrapper is not rendered');
+ assert
+ .dom('[data-test-person-firstName]')
+ .exists({ count: 2 }, 'items are rendered');
+ assert.deepEqual(
+ [...this.element.querySelectorAll('[data-test-person-firstName]')].map(
+ (el) => el.textContent?.trim(),
+ ),
+ ['Mango', 'Van Gogh'],
+ 'item content is rendered in order',
+ );
+ });
+
+ test('renders compound field without chrome when displayContainer is false', async function (assert) {
+ class Name extends FieldDef {
+ @field firstName = contains(StringField);
+ @field lastName = contains(StringField);
+ static embedded = class Embedded extends Component {
+
+
+ <@fields.firstName />
+ <@fields.lastName />
+
+
+ };
+ }
+
+ class Person extends CardDef {
+ @field name = contains(Name);
+ static isolated = class Isolated extends Component {
+
+
+ <@fields.name @displayContainer={{false}} />
+
+
+ };
+ }
+ loader.shimModule(`${testRealmURL}test-cards`, { Person, Name });
+
+ let person = new Person({
+ name: new Name({ firstName: 'Arthur', lastName: 'Dent' }),
+ });
+
+ await renderCard(loader, person, 'isolated');
+
+ assert
+ .dom('[data-test-compound-field-component]')
+ .doesNotExist('compound field wrapper div is not rendered');
+ assert
+ .dom('[data-test-name]')
+ .exists('field content is still rendered')
+ .containsText('Arthur Dent');
+ });
+
test('can #each over a containsMany primitive @fields', async function (assert) {
class Person extends CardDef {
@field firstName = contains(StringField);