Stream multiple 4k videos,
+ download large files in seconds, and dominate online gaming like
+ never before.
+
+
+ <:end>
+
Includes:
+
+
+
✓Cool
+ feature
+
✓Cool
+ feature
+
✓Cool
+ feature
+
✓Cool
+ feature
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/ember-test-app/app/templates/index.hbs b/packages/ember-test-app/app/templates/index.hbs
index c3055f786..e38420b3e 100644
--- a/packages/ember-test-app/app/templates/index.hbs
+++ b/packages/ember-test-app/app/templates/index.hbs
@@ -10,12 +10,14 @@
Marketing Test
-
Form Test
-
+
+ Cards Test
+
+
FAQs Test
@@ -53,7 +55,7 @@
-
+
diff --git a/packages/ember-test-app/tests/integration/components/card-test.ts b/packages/ember-test-app/tests/integration/components/card-test.ts
new file mode 100644
index 000000000..d971e9b8e
--- /dev/null
+++ b/packages/ember-test-app/tests/integration/components/card-test.ts
@@ -0,0 +1,28 @@
+import { assert, module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | components | card', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders the card with the correct content', async function () {
+ await render(hbs`
+
+ <:header>
+
Header content
+
+ <:body>
+
Body content
+
+ `);
+
+ assert.dom('.card').exists('Card is rendered');
+ assert
+ .dom('.card .card-header p')
+ .hasText('Header content', 'Correct content is rendered in card header');
+ assert
+ .dom('.card .card-body p')
+ .hasText('Body content', 'Correct content is rendered in card body');
+ });
+});
diff --git a/packages/ember-test-app/tests/integration/components/mktg/card-test.ts b/packages/ember-test-app/tests/integration/components/mktg/card-test.ts
new file mode 100644
index 000000000..d102f85dc
--- /dev/null
+++ b/packages/ember-test-app/tests/integration/components/mktg/card-test.ts
@@ -0,0 +1,159 @@
+import { assert, module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | mktg/card', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('card correctly renders vertical as default', async function () {
+ await render(hbs`
+
+ <:callout>
+
Callout
+
+ <:start>
+
+
Start section content
+
+
+ <:end>
+
End section content
+
+ `);
+ assert
+ .dom('.card.g-col-12')
+ .exists('Base card is rendered with passed attributes');
+ assert
+ .dom(
+ '.card .card-header .d-flex.flex-column.justify-content-start.align-items-center.bg-white.mb-2',
+ )
+ .exists('Card renders vertically if no @horizontal is passed');
+ assert
+ .dom('.card .card-header div div p')
+ .hasText(
+ 'Title',
+ 'Title content renders in correct order with correct text',
+ );
+ assert
+ .dom('.card .card-header div p:nth-of-type(2)')
+ .hasText(
+ 'Callout',
+ 'Callout renders in correct order with correct content when @leftAlignCallout is not passed',
+ );
+ assert
+ .dom('.card .card-header div div:nth-of-type(2) p')
+ .hasText(
+ 'Subtitle',
+ 'Subtitle content renders in correct order with correct text',
+ );
+
+ assert
+ .dom('.card .card-header div div:nth-of-type(3) p')
+ .hasText(
+ 'Start section content',
+ 'Start section renders when block is present',
+ );
+ assert.dom('.card .card-body').exists('Base card body renders');
+ assert
+ .dom('.card .card-body p')
+ .hasText(
+ 'End section content',
+ 'End section renders when block is present',
+ );
+ await render(hbs`
+
+ <:callout>
+
Callout
+
+ <:start>
+
+
Start section content
+
+
+ <:end>
+
End section content
+
+
+ `);
+ assert
+ .dom(
+ '.card .card-header div .d-flex.flex-column.justify-content-start.w-100.m-0',
+ )
+ .exists(
+ 'Div containing left aligned callout renders when @leftAlignedCallout is true',
+ );
+ assert
+ .dom('.card .card-header div div p')
+ .hasText(
+ 'Callout',
+ 'Callout renders in correct order when @leftAlignCallout is true',
+ );
+ assert
+ .dom('.card .card-header div div p:nth-of-type(2)')
+ .hasText(
+ 'Title',
+ 'Title renders in correct order when @leftAlignCallout is true',
+ );
+ });
+
+ test('card correctly renders horizontal when @horizontal is true', async function () {
+ await render(hbs`
+
+ <:callout>
+
Callout
+
+ <:start>
+
+
Start section content
+
+
+ <:end>
+
End section content
+
+ `);
+
+ assert.notOk(
+ document.querySelector('.card-header'),
+ 'Card header does not render when @horizontal is true',
+ );
+ assert.dom('.card .card-body').exists('Base card body renders');
+ assert
+ .dom('.card .card-body div div div p')
+ .hasText('Title', 'Title renders in correct order with correct text');
+ assert
+ .dom('.card .card-body div div div p:nth-of-type(2)')
+ .hasText(
+ 'Callout',
+ 'Callout renders in correct order with correct content',
+ );
+ assert
+ .dom('.card .card-body div div div div p')
+ .hasText(
+ 'Start section content',
+ 'Start section renders in correct order when present',
+ );
+ assert
+ .dom(
+ '.card .card-body div:nth-of-type(2) .vr.d-none.d-md-flex.text-body-secondary',
+ )
+ .exists('Divider renders if end block is present');
+ assert
+ .dom('.card .card-body div:nth-of-type(3) p')
+ .hasText('End section content', 'End section renders when present');
+ });
+});
diff --git a/packages/ember/package.json b/packages/ember/package.json
index 3825f9325..ac37f753f 100644
--- a/packages/ember/package.json
+++ b/packages/ember/package.json
@@ -134,6 +134,7 @@
"./components/alert.js": "./dist/_app_/components/alert.js",
"./components/button-group.js": "./dist/_app_/components/button-group.js",
"./components/button.js": "./dist/_app_/components/button.js",
+ "./components/card.js": "./dist/_app_/components/card.js",
"./components/footer.js": "./dist/_app_/components/footer.js",
"./components/header.js": "./dist/_app_/components/header.js",
"./components/loading-indicator.js": "./dist/_app_/components/loading-indicator.js",
diff --git a/packages/ember/src/components/card.gts b/packages/ember/src/components/card.gts
new file mode 100644
index 000000000..055febeba
--- /dev/null
+++ b/packages/ember/src/components/card.gts
@@ -0,0 +1,48 @@
+import Component from '@glimmer/component';
+import { on } from '@ember/modifier';
+import { action } from '@ember/object';
+
+interface CardSignature {
+ Element: HTMLDivElement;
+ Args: {
+ isClickable?: boolean;
+ // eslint-disable-next-line no-unused-vars
+ onClick?: (evt: MouseEvent) => unknown;
+ };
+ Blocks: {
+ header: [];
+ body: [];
+ };
+}
+
+export default class Card extends Component {
+ @action
+ onClick(evt: MouseEvent) {
+ evt?.preventDefault();
+ evt?.stopPropagation();
+
+ this.args.onClick?.(evt);
+ }
+
+
+
+ {{#if (has-block "header")}}
+
+ {{yield to="header"}}
+
+ {{/if}}
+ {{#if (has-block "body")}}
+
+ {{yield to="body"}}
+
+ {{/if}}
+
+
+}
diff --git a/packages/ember/src/components/mktg/card-container.gts b/packages/ember/src/components/mktg/card-container.gts
index 9965eaece..2d78c5526 100644
--- a/packages/ember/src/components/mktg/card-container.gts
+++ b/packages/ember/src/components/mktg/card-container.gts
@@ -1,7 +1,8 @@
import type { TOC } from '@ember/component/template-only';
import { hash } from '@ember/helper';
import type { ComponentLike } from '@glint/template';
-import Card from './card.js';
+import MktgCard from './card.gts';
+import Card from '../card.gts';
interface CardContainerSignature {
Element: HTMLDivElement;
@@ -9,13 +10,14 @@ interface CardContainerSignature {
default: [
{
Card: ComponentLike;
+ MktgCard: ComponentLike;
},
];
};
}
const CardContainer: TOC =