Skip to content

Commit 1e7d0fa

Browse files
authored
Add new 'card' page to example site + new tests for cards (#305)
* Cards: Replace with data-grid * Cards: Add new page to example site * Cards: Add tests * Cards: Small tweaks * Cards: Updated card params in example site * Cards: Fix bugs with the card due to merge
1 parent 54712a3 commit 1e7d0fa

File tree

7 files changed

+218
-8
lines changed

7 files changed

+218
-8
lines changed

assets/css/v2/style.css

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1482,13 +1482,21 @@ h6:has(a):hover {
14821482

14831483
&.featured-section {
14841484
/* Hide all the cards past 3 if it is a featured card section */
1485-
.card-section-content > *:nth-child(n + 4 of div.card) {
1485+
.card-section-content > *:nth-child(n + 4 of a.card) {
14861486
display: none;
14871487
}
14881488
}
14891489
}
14901490

14911491
.card {
1492+
color: oklch(var(--color-foreground));
1493+
text-decoration: none;
1494+
order: 2;
1495+
1496+
&.featured-card {
1497+
order: 1;
1498+
}
1499+
14921500
.card-container {
14931501
display: flex;
14941502
flex-direction: column;
@@ -1500,6 +1508,7 @@ h6:has(a):hover {
15001508
flex-direction: row;
15011509
gap: 1rem;
15021510
align-items: center;
1511+
color: oklch(var(--color-brand));
15031512

15041513
.card-brand-icon {
15051514
height: 20px;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
description: Card usage
3+
title: Cards
4+
weight: 300
5+
---
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
---
2+
description: Card usage
3+
title: Usage
4+
weight: 100
5+
---
6+
## General usage
7+
The structure of a card looks like the following:
8+
```plaintext
9+
<card-layout >
10+
<card-section>
11+
<card title="SOME_TITLE">SOME CONTENT icon="SOME_LUCIDE_ICON"><\card>
12+
...
13+
<card>SOME CONTENT<\card>
14+
</card-section>
15+
</card-layout >
16+
```
17+
18+
and will render as the following:
19+
<div data-testid="cards-test__basic">
20+
{{<card-layout >}}
21+
{{<card-section>}}
22+
{{<card title="SOME_TITLE">}}
23+
SOME CONTENT
24+
{{</card >}}
25+
{{</card-section>}}
26+
{{</card-layout >}}
27+
</div>
28+
29+
### Params
30+
To support customization, there are also some params you can add to the shortcode `<card>` such as `title`, `titleUrl`, `icon`, `brandIcon`, `isFeaturedCard`, `isFullSize`.
31+
32+
* `title` (required) - Title of the card.
33+
<br>
34+
* `titleUrl` (optional) - URL for the card.
35+
<br>
36+
* `icon` (optional) - Custom icon using <a href="https://lucide.dev/icons/">lucide</a> icons.
37+
<br>
38+
* `brandIcon` (optional) - Custom icon using image from `nginx-hugo-theme/static/images/icons`.
39+
* Usage: `<card brandIcon="NGINX-App-Protect-WAF-product-icon"...>`
40+
<br>
41+
* `isFeaturedCard` (optional) - Boolean indicating whether or not the card should be the first one and full size. By default, false.
42+
* Usage: `<card isFeaturedCard="true"...>`
43+
<br>
44+
* `isFullSize` (optional) - Boolean indicating whether or not the card should be full size. By default, cards are half sized.
45+
* Usage: `<card isFullSize="true"...>`
46+
47+
For the `<card-section>`, there are some params you can add such as `title`, `isFeaturedSection`, and `showAsCards`.
48+
* `title` - Title of the section.
49+
<br>
50+
* `isFeaturedSection` (optional) - Boolean indicating whether or not the section is a featured one - will discuss later down the page. By default, false.
51+
<br>
52+
* `showAsCards` (optional) - Boolean indicating whether or not the cards in the section should appear with borders. By default, false.
53+
54+
### Additional Information
55+
While it may come immediate, you can't use a `<card>` shortcode on its own in your markdown or else the build will fail. This is because if you call a card, there is no way to structure it in a writer-friendly customizable way.
56+
57+
## Show as cards
58+
As you can see from the above example in 'General usage', it renders as a plain box that does not resemble a card. To change the appearance and render as a card, use the param `showAsCards` in the shortcode `<card-section>`. The usage as seen below:
59+
```plaintext
60+
<card-layout >
61+
<card-section showAsCards="true">
62+
<card title="SOME_TITLE">SOME CONTENT icon="SOME_LUCIDE_ICON"><\card>
63+
...
64+
<card>SOME CONTENT<\card>
65+
</card-section>
66+
</card-layout >
67+
```
68+
and will render as the following:
69+
<div data-testid="cards-test__showAsCards">
70+
{{<card-layout >}}
71+
{{<card-section showAsCards="true">}}
72+
{{<card title="SOME_TITLE">}}
73+
SOME CONTENT
74+
{{</card >}}
75+
{{</card-section>}}
76+
{{</card-layout >}}
77+
</div>
78+
79+
## Featured Section
80+
Denoted by the param `isFeaturedSection` in the shortcode `<card-layout>`, this block of cards can contain only up to three cards.
81+
82+
<div data-testid="cards-test__featuredSection">
83+
{{<card-layout >}}
84+
{{<card-section showAsCards="true" isFeaturedSection="true">}}
85+
{{<card title="Everything" titleUrl="everything" icon="circle-dot-dashed">}}
86+
All shortcodes in one page.
87+
{{</card >}}
88+
{{<card title="Call Out usages" titleUrl="call-out/all-callouts/" icon="message-square">}}
89+
Examples for call-out shortcode
90+
{{</card >}}
91+
{{<card title="Code Block usages" titleUrl="code-blocks/code-blocks-highlighting/" icon="code">}}
92+
Examples for codeblock shortcode
93+
{{</card >}}
94+
{{<card title="Code Block usages" titleUrl="code-blocks/code-blocks-highlighting/" icon="code">}}
95+
Examples for codeblock shortcode
96+
{{</card >}}
97+
{{</card-section>}}
98+
{{</card-layout >}}
99+
</div>
100+
101+
## Featured Card
102+
Denoted by the param `isFeaturedCard` in the shortcode `<card>`, this will push the card up to very top of the section and make it full length.
103+
<div data-testid="cards-test__featuredCard">
104+
{{<card-layout >}}
105+
{{<card-section showAsCards="true">}}
106+
{{<card title="Everything" titleUrl="everything" icon="circle-dot-dashed">}}
107+
All shortcodes in one page.
108+
{{</card >}}
109+
{{<card title="Call Out usages" titleUrl="call-out/all-callouts/" icon="message-square">}}
110+
Examples for call-out shortcode
111+
{{</card >}}
112+
{{<card title="Code Block usages" titleUrl="code-blocks/code-blocks-highlighting/" icon="code" isFeaturedCard="true">}}
113+
Examples for codeblock shortcode
114+
{{</card >}}
115+
{{<card title="Code Block usages" titleUrl="code-blocks/code-blocks-highlighting/" icon="code">}}
116+
Examples for codeblock shortcode
117+
{{</card >}}
118+
{{</card-section>}}
119+
{{</card-layout >}}
120+
</div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
<!-- Render the main content first with modifications, then render the custom content -->
2-
<div class="card-layout">{{- .Inner | markdownify -}}</div>
2+
<div class="card-layout" data-testid="card-layout">{{- .Inner | markdownify -}}</div>

layouts/shortcodes/card-section.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{{ $title := .Get "title" }}
22
{{ $isFeaturedSectionParam := .Get "isFeaturedSection" | default "false" }}
33
{{ $showAsCardsParam := .Get "showAsCards" | default "false"}}
4+
45
{{- /* Validate the parameter strictly */ -}}
56
{{- if not (in (slice "true" "false") $showAsCardsParam) -}}
67
{{- warnf "The '<card-section>' Shortcode parameter 'showAsCards' must be 'true' or 'false', but got: '%s'. This will now default to 'false'" $showAsCardsParam -}}
@@ -15,14 +16,18 @@
1516
{{- end -}}
1617
{{- $isFeaturedSection := cond (eq $isFeaturedSectionParam "true") "true" "false" -}}
1718
{{- $class := "card-grid" -}}
19+
20+
{{- /* Get number of cards */ -}}
21+
{{ $cardCount := len (findRE "<div\\s+class=\"card\"[\\s\\S]*?>" .Inner) }}
22+
1823
{{- /* Validate that the parent is card-layout */ -}}
1924
{{ if eq .Parent.Name "card-layout"}}
20-
<div class="card-section{{if eq $isFeaturedSection "true"}}featured-section{{ end }}">
25+
<div class="card-section {{if eq $isFeaturedSection "true"}}featured-section{{ end }}" data-testid="{{if eq $isFeaturedSection "true"}}card-section__featured-section{{else}}card-section{{ end }}">
2126
{{- if $title -}}
2227
<strong class="card-section-title">{{- $title -}}</strong>
23-
<div class="card-section-content{{ if eq $showAsCards "true" }} {{ $class }}{{ end }}">{{ .Inner }}</div>
28+
<div class="card-section-content{{ if eq $showAsCards "true" }} {{ $class }}{{ end }}" data-testid="{{ if eq $showAsCards "true" }}card-section-content__card-grid{{else}}card-section-content{{ end }}">{{ .Inner }}</div>
2429
{{ else }}
25-
<div class="card-section-content{{ if eq $showAsCards "true" }} {{ $class }}{{ end }}">{{ .Inner }}</div>
30+
<div class="card-section-content{{ if eq $showAsCards "true" }} {{ $class }}{{ end }}" data-testid="{{ if eq $showAsCards "true" }}card-section-content__card-grid{{else}}card-section-content{{ end }}">{{ .Inner }}</div>
2631
{{ end }}
2732
</div>
2833
{{ else }}

layouts/shortcodes/card.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
{{- $titleUrl := .Get "titleUrl" | default "." -}}
33
{{- $icon := .Get "icon" | default "book-open" -}}
44
{{- $brandIcon := .Get "brandIcon" -}}
5-
{{- $isFeaturedParam := .Get "isFeatured" | default "false" }}
5+
{{- $isFeaturedParam := .Get "isFeaturedCard" | default "false" }}
66
{{- $isFullSizeParam := .Get "isFullSize" | default "false" -}}
77
{{- $isLandingParam := .Get "isLanding" | default "false" }}
88

99
{{- /* Validate the parameter strictly */ -}}
1010
{{- if not (in (slice "true" "false") $isFeaturedParam) -}}
11-
{{- warnf "The '<card>' Shortcode parameter 'isFeatured' must be 'true' or 'false', but got: '%s'. This will now default to 'false'" $isFeaturedParam -}}
11+
{{- warnf "The '<card>' Shortcode parameter 'isFeaturedCard' must be 'true' or 'false', but got: '%s'. This will now default to 'false'" $isFeaturedParam -}}
1212
{{- end -}}
1313
{{- $isFeatured := cond (eq $isFeaturedParam "true") "true" "false" -}}
1414

@@ -34,7 +34,7 @@
3434

3535
{{- /* Validate that the parent is card-section and under 3 cards */ -}}
3636
{{- if (eq .Parent.Name "card-section") -}}
37-
<a href="{{- $titleUrl -}}" alt="{{- $title -}}" class="card{{ if eq $isFeatured "true" }} featured-card{{ end }}" data-grid="{{ $dataGrid }}">
37+
<a href="{{- $titleUrl -}}" alt="{{- $title -}}" class="card{{ if eq $isFeatured "true" }} featured-card{{ end }}" data-grid="{{ $dataGrid }}" data-testid="{{ if eq $isFeatured "true" }}card__featured-card{{else}}card{{ end }}">
3838
<div class="card-container">
3939
{{- if $title -}}
4040
<div class="card-header">

tests/src/cards.spec.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
test.describe('Testing for cards shortcode', () => {
4+
test.beforeEach(async ({ page }) => {
5+
const cardsUrl = 'test-product/cards/permitted';
6+
await page.goto(`/${cardsUrl}`);
7+
});
8+
9+
test('should test basic section', async ({ page }) => {
10+
const section = await page.locator('data-testid=cards-test__basic');
11+
const basicCode = await section.locator('data-testid=card-section-content');
12+
const showAsCardCode = await section.locator(
13+
'data-testid=card-section-content__card-grid'
14+
);
15+
16+
expect(await basicCode.count()).toBeTruthy();
17+
expect(await showAsCardCode.count()).toBe(0);
18+
});
19+
20+
test('should test showAsCard section', async ({ page }) => {
21+
const section = await page.locator('data-testid=cards-test__showAsCards');
22+
const showAsCardCode = await section.locator(
23+
'data-testid=card-section-content__card-grid'
24+
);
25+
26+
expect(await showAsCardCode.count()).toBeTruthy();
27+
});
28+
29+
test('should test featured section', async ({ page }) => {
30+
const section = await page.locator(
31+
'data-testid=cards-test__featuredSection'
32+
);
33+
const featuredSection = await section.locator(
34+
'data-testid=card-section__featured-section'
35+
);
36+
const cards = await (
37+
await featuredSection.locator('data-testid=card')
38+
).all();
39+
40+
// Test featured section exists AND there are less than or equal to 3 cards.
41+
expect(await featuredSection.count()).toBeTruthy();
42+
const visibleCards = [];
43+
for (const card of cards) {
44+
if (await card.isVisible()) {
45+
visibleCards.push(card);
46+
}
47+
}
48+
expect(visibleCards.length).toBeLessThanOrEqual(3);
49+
});
50+
51+
test('should test featured card', async ({ page }) => {
52+
const section = await page.locator('data-testid=cards-test__featuredCard');
53+
const featuredCard = await section.locator(
54+
'data-testid=card__featured-card'
55+
);
56+
const cards = await (await section.locator('data-testid=card')).all();
57+
const featuredCardOrder = await featuredCard.evaluate((el) => {
58+
return window.getComputedStyle(el).getPropertyValue('order');
59+
});
60+
61+
// Test featured card exist AND is the first one in the order
62+
expect(await featuredCard.count()).toBeTruthy();
63+
expect(featuredCardOrder).toBe('1');
64+
for (const card of cards) {
65+
const order = await card.evaluate((el) => {
66+
return window.getComputedStyle(el).getPropertyValue('order');
67+
});
68+
expect(order).toBe('2');
69+
}
70+
});
71+
});

0 commit comments

Comments
 (0)