Skip to content

Commit

Permalink
feat: Implement base and mktg card components (#31)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ChesneyJulian authored Aug 1, 2024
1 parent e9656c7 commit 044d433
Show file tree
Hide file tree
Showing 11 changed files with 483 additions and 46 deletions.
7 changes: 7 additions & 0 deletions packages/ember-test-app/app/controllers/cards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Controller from '@ember/controller';

export default class CardsController extends Controller {
clickHandler() {
console.log('clickHandler called');
}
}
169 changes: 156 additions & 13 deletions packages/ember-test-app/app/templates/cards.hbs
Original file line number Diff line number Diff line change
@@ -1,15 +1,158 @@
<div class="container">
<p class="py-3">Row on desktop, stacked on mobile</p>
{{!-- template-lint-disable no-inline-styles --}}
<Mktg::CardContainer class="bg-info rounded" style="--bs-gap: .5rem;" as |Container|>
<Container.Card class="g-col-12 g-col-md-4" />
<Container.Card class="g-col-12 g-col-md-4" />
<Container.Card class="g-col-12 g-col-md-4" />
</Mktg::CardContainer>
<p class="py-3">stacked on mobile and desktop</p>
<Mktg::CardContainer class="bg-secondary" as |Container|>
<Container.Card class="g-col-12" />
<Container.Card class="g-col-12" />
<Container.Card class="g-col-12" />
<div data-theme="marketing" class="container">
<p class="py-3 fw-bold">Row on desktop, stacked on mobile</p>
{{! template-lint-disable no-inline-styles }}
<Mktg::CardContainer style="--bs-gap: .5rem;" as |Container|>
<Container.Card
@isClickable={{true}}
@onClick={{this.clickHandler}}
class="g-col-12 g-col-md-4"
>
<:header>
<p>Here is a title</p>
</:header>
<:body>
<p>Here is some body content</p>
</:body>
</Container.Card>
<Container.Card
@isClickable={{true}}
@onClick={{this.clickHandler}}
class="g-col-12 g-col-md-4"
>
<:header>
<p>Here is a title</p>
</:header>
<:body>
<p>Here is some body content</p>
</:body>
</Container.Card>
<Container.Card
@isClickable={{true}}
@onClick={{this.clickHandler}}
class="g-col-12 g-col-md-4"
>
<:header>
<p>Here is a title</p>
</:header>
<:body>
<p>Here is some body content</p>
</:body>
</Container.Card>
</Mktg::CardContainer>
<div class="container">
<p class="pt-3 fw-bold">Vertical card with right aligned price</p>
<Mktg::CardContainer class="justify-content-center" as |Container|>
<Container.MktgCard
class="g-col-12"
@title="The Gig"
@subtitle="Experience lightning-fast internet connectivity"
>
<:start>
<div class="d-flex w-100 align-items-start">
<p>Start Section can go here if you'd like</p>
</div>
</:start>
<:callout>
<p
class="d-flex align-self-start align-self-md-center fs-1 fw-bold m-0"
>&dollar;315<span class="fs-5 align-self-end mb-2">/mo</span></p>
</:callout>
<:end>
<p class="card-text mt-2">Includes:</p>
<div class="container">
<div class="row row-cols-2">
<p class="col-12 col-md-6"><span
class="me-2 fw-bold"
>&#x2713;</span>Cool feature</p>
<p class="col-12 col-md-6"><span
class="me-2 fw-bold"
>&#x2713;</span>Cool feature</p>
<p class="col-12 col-md-6"><span
class="me-2 fw-bold"
>&#x2713;</span>Cool feature</p>
<p class="col-12 col-md-6"><span
class="me-2 fw-bold"
>&#x2713;</span>Cool feature</p>
</div>
</div>
<Button @text="Add to Bundle" class="btn-primary w-100 mt-2" />
</:end>
</Container.MktgCard>
</Mktg::CardContainer>
<p class="pt-3 fw-bold">Vertical card with left aligned price</p>
<Mktg::CardContainer class="justify-content-center" as |Container|>
<Container.MktgCard
class="g-col-12"
@title="The Gig"
@subtitle="Experience lightning-fast internet connectivity"
@leftAlignCallout={{true}}
>
<:callout>
<p class="d-flex fs-1 fw-bold m-0">&dollar;300/mo</p>
</:callout>
<:end>
<p class="card-text mt-2">Includes:</p>
<div class="container">
<div class="row row-cols-2">
<p class="col-12 col-md-6"><span
class="me-2 fw-bold"
>&#x2713;</span>Cool feature</p>
<p class="col-12 col-md-6"><span
class="me-2 fw-bold"
>&#x2713;</span>Cool feature</p>
<p class="col-12 col-md-6"><span
class="me-2 fw-bold"
>&#x2713;</span>Cool feature</p>
<p class="col-12 col-md-6"><span
class="me-2 fw-bold"
>&#x2713;</span>Cool feature</p>
</div>
</div>
<Button @text="Add to Bundle" class="btn-primary w-100 mt-2" />
</:end>
</Container.MktgCard>
</Mktg::CardContainer>
</div>

<div class="container">
<p class="pt-3 fw-bold">Horizontal card</p>
<Mktg::CardContainer as |Container|>
<Container.MktgCard
@title="The Gig 10"
@horizontal={{true}}
class="g-col-12"
>
<:callout>
<p
class="card-title d-flex align-self-start fs-1 fw-bold m-0 pb-2"
>&dollar;300/mo</p>
</:callout>
<:start>
<p class="card-text text-body-secondary">Stream multiple 4k videos,
download large files in seconds, and dominate online gaming like
never before.</p>
<Button
@text="Get Started"
class="bg-primary text-white w-100 mt-2"
/>
</:start>
<:end>
<p class="card-text mt-2">Includes:</p>
<div class="container">
<div class="row">
<p class="col-12"><span class="me-2 fw-bold">&#x2713;</span>Cool
feature</p>
<p class="col-12"><span class="me-2 fw-bold">&#x2713;</span>Cool
feature</p>
<p class="col-12"><span class="me-2 fw-bold">&#x2713;</span>Cool
feature</p>
<p class="col-12"><span class="me-2 fw-bold">&#x2713;</span>Cool
feature</p>
</div>
</div>
</:end>
</Container.MktgCard>
</Mktg::CardContainer>
</div>
<div class="p-5"></div>
</div>
8 changes: 5 additions & 3 deletions packages/ember-test-app/app/templates/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
Marketing Test
<i class="text-success bi-currency-dollar"></i>
</LinkTo>

<LinkTo @route="form" class="btn btn-outline-primary" role="link">
Form Test
<i class="text-danger bi-input-cursor-text"></i>
</LinkTo>

<LinkTo @route="cards" class="btn btn-outline-info" role="link">
Cards Test
<i class="text-dark bi-wallet2"></i>
</LinkTo>
<LinkTo @route="faq" class="btn btn-outline-success" role="link">
FAQs Test
<i class="bi-plus"></i>
Expand Down Expand Up @@ -53,7 +55,7 @@
</Progress>
</div>
<div class="my-3">
<div class="d-flex align-items-center vw-100 p-3 text-secondary">
<div class="d-flex align-items-center p-3 text-secondary">
<LoadingIndicator class="ms-auto" @showLabel={{true}} />
</div>
</div>
Expand Down
28 changes: 28 additions & 0 deletions packages/ember-test-app/tests/integration/components/card-test.ts
Original file line number Diff line number Diff line change
@@ -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`
<Card>
<:header>
<p>Header content</p>
</:header>
<:body>
<p>Body content</p>
</:body>
</Card>`);

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');
});
});
159 changes: 159 additions & 0 deletions packages/ember-test-app/tests/integration/components/mktg/card-test.ts
Original file line number Diff line number Diff line change
@@ -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`
<Mktg::Card
class="g-col-12"
@title="Title"
@subtitle="Subtitle"
>
<:callout>
<p>Callout</p>
</:callout>
<:start>
<div>
<p>Start section content</p>
</div>
</:start>
<:end>
<p>End section content</p>
</:end>
</Mktg::Card>`);
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`
<Mktg::Card
class="g-col-12"
@title="Title"
@subtitle="Subtitle"
@leftAlignCallout={{true}}
>
<:callout>
<p>Callout</p>
</:callout>
<:start>
<div>
<p>Start section content</p>
</div>
</:start>
<:end>
<p>End section content</p>
</:end>
</Mktg::Card>
`);
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`
<Mktg::Card
class="g-col-12"
@title="Title"
@subtitle="Subtitle"
@horizontal={{true}}
>
<:callout>
<p>Callout</p>
</:callout>
<:start>
<div>
<p>Start section content</p>
</div>
</:start>
<:end>
<p>End section content</p>
</:end>
</Mktg::Card>`);

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');
});
});
Loading

0 comments on commit 044d433

Please sign in to comment.