From 044d433c7135829e8e72257db140fdd76c9ac550 Mon Sep 17 00:00:00 2001 From: Chesney Julian <121467322+ChesneyJulian@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:05:00 -0400 Subject: [PATCH] feat: Implement base and mktg card components (#31) * feat: Implement base card component for header and body content * feat: Implement vertical mktg card * feat: Implement horizontal card component * feat: Add parameter to left align price in vertical card * feat: Implement tests for vertical card * feat: Implement tests for horizontal card * refactor: Add button role to base card div * refactor: Conditionally render divider in horizontal card * refactor: Rebase and add link on index.hbs * refactor: Combine horizontal and vertical card and address requested changes * fix: Fix isClickable implementation * refactor: Update tests and clean test app * refactor: Clean up empty getters and conditionals * refactor: Fix formatting after rebase --- .../ember-test-app/app/controllers/cards.ts | 7 + .../ember-test-app/app/templates/cards.hbs | 169 ++++++++++++++++-- .../ember-test-app/app/templates/index.hbs | 8 +- .../tests/integration/components/card-test.ts | 28 +++ .../integration/components/mktg/card-test.ts | 159 ++++++++++++++++ packages/ember/package.json | 1 + packages/ember/src/components/card.gts | 48 +++++ .../src/components/mktg/card-container.gts | 6 +- packages/ember/src/components/mktg/card.gts | 75 ++++++++ packages/ember/src/components/mktg/card.hbs | 20 --- packages/ember/src/components/mktg/card.js | 8 - 11 files changed, 483 insertions(+), 46 deletions(-) create mode 100644 packages/ember-test-app/app/controllers/cards.ts create mode 100644 packages/ember-test-app/tests/integration/components/card-test.ts create mode 100644 packages/ember-test-app/tests/integration/components/mktg/card-test.ts create mode 100644 packages/ember/src/components/card.gts create mode 100644 packages/ember/src/components/mktg/card.gts delete mode 100644 packages/ember/src/components/mktg/card.hbs delete mode 100644 packages/ember/src/components/mktg/card.js diff --git a/packages/ember-test-app/app/controllers/cards.ts b/packages/ember-test-app/app/controllers/cards.ts new file mode 100644 index 000000000..9471b7cc0 --- /dev/null +++ b/packages/ember-test-app/app/controllers/cards.ts @@ -0,0 +1,7 @@ +import Controller from '@ember/controller'; + +export default class CardsController extends Controller { + clickHandler() { + console.log('clickHandler called'); + } +} diff --git a/packages/ember-test-app/app/templates/cards.hbs b/packages/ember-test-app/app/templates/cards.hbs index 721b62a3e..70537bd3f 100644 --- a/packages/ember-test-app/app/templates/cards.hbs +++ b/packages/ember-test-app/app/templates/cards.hbs @@ -1,15 +1,158 @@ -
-

Row on desktop, stacked on mobile

- {{!-- template-lint-disable no-inline-styles --}} - - - - - -

stacked on mobile and desktop

- - - - +
+

Row on desktop, stacked on mobile

+ {{! template-lint-disable no-inline-styles }} + + + <:header> +

Here is a title

+ + <:body> +

Here is some body content

+ +
+ + <:header> +

Here is a title

+ + <:body> +

Here is some body content

+ +
+ + <:header> +

Here is a title

+ + <:body> +

Here is some body content

+ +
+
+

Vertical card with right aligned price

+ + + <:start> +
+

Start Section can go here if you'd like

+
+ + <:callout> +

$315/mo

+ + <:end> +

Includes:

+
+
+

Cool feature

+

Cool feature

+

Cool feature

+

Cool feature

+
+
+
+ +
+

Horizontal card

+ + + <:callout> +

$300/mo

+ + <:start> +

Stream multiple 4k videos, + download large files in seconds, and dominate online gaming like + never before.

+
+
\ 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); + } + + +} 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 = ; diff --git a/packages/ember/src/components/mktg/card.gts b/packages/ember/src/components/mktg/card.gts new file mode 100644 index 000000000..48aff10f6 --- /dev/null +++ b/packages/ember/src/components/mktg/card.gts @@ -0,0 +1,75 @@ +import type { TOC } from '@ember/component/template-only'; +import Card from '../card.gts'; + +interface CardSignature { + Element: HTMLDivElement; + Args: { + title?: string; + subtitle?: string; + leftAlignCallout?: boolean; + horizontal?: boolean; + }; + Blocks: { + start: []; + end: []; + callout: []; + }; +} + +const MktgCard: TOC = ; + +export default MktgCard; diff --git a/packages/ember/src/components/mktg/card.hbs b/packages/ember/src/components/mktg/card.hbs deleted file mode 100644 index dac4b49f1..000000000 --- a/packages/ember/src/components/mktg/card.hbs +++ /dev/null @@ -1,20 +0,0 @@ -
-
-

$1/mo

-
Product name
-
Product description
-
-
-
Features:
- -
    -
  • Feature 1
  • -
  • Feature 2
  • -
  • Feature 3
  • -
-
-
- -
-
-
\ No newline at end of file diff --git a/packages/ember/src/components/mktg/card.js b/packages/ember/src/components/mktg/card.js deleted file mode 100644 index 8e16b30dc..000000000 --- a/packages/ember/src/components/mktg/card.js +++ /dev/null @@ -1,8 +0,0 @@ -import { action } from '@ember/object'; -import Component from '@glimmer/component'; -export default class MarketingCardComponent extends Component { - @action - onClick() { - this.args.onClick?.(); - } -}