From 98989c15f53a1e36012b6af85f3af6dd95f9b96d Mon Sep 17 00:00:00 2001 From: Lam Nguyen Date: Mon, 7 Jul 2025 13:47:27 -0700 Subject: [PATCH 1/4] Cards: Replace with data-grid --- assets/css/v2/style.css | 83 +++++++++++++--------------- layouts/shortcodes/card-section.html | 9 ++- layouts/shortcodes/card.html | 21 +++++-- 3 files changed, 63 insertions(+), 50 deletions(-) diff --git a/assets/css/v2/style.css b/assets/css/v2/style.css index 718a2ad..aafdb3d 100644 --- a/assets/css/v2/style.css +++ b/assets/css/v2/style.css @@ -880,6 +880,9 @@ nav { [data-grid="last-third"] { grid-column: 1; } + [data-grid="halve"] { + grid-column: 1; + } @media (min-width: 55rem) { max-width: var(--content-max-width); @@ -905,6 +908,10 @@ nav { [data-grid="last-third"] { grid-column: 8 / -1; } + + [data-grid="halve"] { + grid-column: span 6; + } } } @@ -1451,7 +1458,7 @@ h6:has(a):hover { } /* Landing page cards */ -.text-content .card-layout { +.card-layout { grid-column: 1; .card-section { @@ -1463,65 +1470,53 @@ h6:has(a):hover { font-weight: 500; } - /* Hide all the cards past 3 if it is a featured card section */ &.featured-section { - .card-section-content.card-grid - > *:nth-child(n + 4 of div.card-container) { + /* Hide all the cards past 3 if it is a featured card section */ + .card-section-content > *:nth-child(n + 4 of div.card) { display: none; } } } -} -/* Optional grid layout */ -.card-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(33%, 1fr)); - gap: 1.5rem; + .card { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 1rem; - .card-container { - border: 1px solid oklch(var(--color-codeblock-border)); - box-shadow: 3px 3px 0px oklch(var(--color-shadow)); - padding: 1rem; + .card-header { + display: flex; + flex-direction: row; + gap: 0.5rem; - &.featured-card { - /* If there is a need for a featured card, only the featured card should be full length */ - grid-column: 1 / -1; + .card-brand-icon { + height: 20px; + width: auto; + } - /* If there is a featured card AND two cards, the last one should be full length */ - ~ .card-container:nth-child(2n):last-child { - grid-column: 1 / -1; + h2 { + padding: 0; + margin: 0; + font-size: 1rem; } } } - - /* If there is no featured card, last card that is the 3rd one should be full width */ - &:not(:has(.featured-card)) *:nth-child(n + 3 of div.card-container) { - grid-column: 1 / -1; - } } -.card-container { - display: flex; - flex-direction: column; - gap: 0.5rem; - margin-bottom: 1rem; - - .card-header { - display: flex; - flex-direction: row; - gap: 0.5rem; +/* Optional grid layout */ +.card-grid { + display: grid; + grid-template-columns: repeat(var(--grid-columns), 1fr); + gap: 1.5rem; - .card-brand-icon { - height: 20px; - width: auto; - } + @media (max-width: 68rem) { + grid-template-columns: 1fr; + } - h2 { - padding: 0; - margin: 0; - font-size: 1rem; - } + .card { + border: 1px solid oklch(var(--color-codeblock-border)); + box-shadow: 3px 3px 0px oklch(var(--color-shadow)); + padding: 1rem; } } diff --git a/layouts/shortcodes/card-section.html b/layouts/shortcodes/card-section.html index c05caa9..52335d2 100644 --- a/layouts/shortcodes/card-section.html +++ b/layouts/shortcodes/card-section.html @@ -1,6 +1,7 @@ {{ $title := .Get "title" }} {{ $isFeaturedSectionParam := .Get "isFeaturedSection" | default "false" }} {{ $showAsCardsParam := .Get "showAsCards" | default "false"}} + {{- /* Validate the parameter strictly */ -}} {{- if not (in (slice "true" "false") $showAsCardsParam) -}} {{- warnf "The '' Shortcode parameter 'showAsCards' must be 'true' or 'false', but got: '%s'. This will now default to 'false'" $showAsCardsParam -}} @@ -15,14 +16,18 @@ {{- end -}} {{- $isFeaturedSection := cond (eq $isFeaturedSectionParam "true") "true" "false" -}} {{- $class := "card-grid" -}} + +{{- /* Get number of cards */ -}} +{{ $cardCount := len (findRE "" .Inner) }} + {{- /* Validate that the parent is card-layout */ -}} {{ if eq .Parent.Name "card-layout"}}
{{- if $title -}} {{- $title -}} -
{{- .Inner -}}
+
{{- .Inner -}}
{{ else }} -
{{ .Inner }}
+
{{ .Inner }}
{{ end }}
{{ else }} diff --git a/layouts/shortcodes/card.html b/layouts/shortcodes/card.html index b4d5587..5b5b2dd 100644 --- a/layouts/shortcodes/card.html +++ b/layouts/shortcodes/card.html @@ -3,17 +3,30 @@ {{- $icon := .Get "icon" | default "book-open" -}} {{- $brandIcon := .Get "brandIcon" -}} {{- $isFeaturedParam := .Get "isFeatured" | default "false" }} +{{- $isFullSizeParam := .Get "isFullSize" | default "false" -}} + {{- /* Validate the parameter strictly */ -}} {{- if not (in (slice "true" "false") $isFeaturedParam) -}} {{- warnf "The '' Shortcode parameter 'isFeatured' must be 'true' or 'false', but got: '%s'. This will now default to 'false'" $isFeaturedParam -}} {{- end -}} {{- $isFeatured := cond (eq $isFeaturedParam "true") "true" "false" -}} -{{- $current := .Page.Scratch.Get "cards" | default (slice) -}} -{{- $newCard := dict "title" ($title) "content" (.Inner) -}} -{{- .Page.Scratch.Set "cards" ($current | append $newCard) -}} + +{{- if not (in (slice "true" "false") $isFullSizeParam) -}} + {{- warnf "The '' Shortcode parameter 'isFullSize' must be 'true' or 'false', but got: '%s'. This will now default to 'false'" $isFullSizeParam -}} +{{- end -}} +{{- $isFullSize := cond (eq $isFullSizeParam "true") "true" "false" -}} + +{{- /* Set up the positioning */ -}} +{{ $dataGrid := "" }} +{{ if or (eq $isFullSize "true") (eq $isFeatured "true") }} + {{ $dataGrid = "wide"}} +{{ else }} + {{ $dataGrid = "halve"}} +{{ end }} + {{- /* Validate that the parent is card-section and under 3 cards */ -}} {{- if (eq .Parent.Name "card-section") -}} -
+
{{- if $title -}}
{{- if $brandIcon -}} From 01e57efbc4328807d61667c6e51d76a1646912bd Mon Sep 17 00:00:00 2001 From: Lam Nguyen Date: Mon, 7 Jul 2025 13:51:18 -0700 Subject: [PATCH 2/4] Cards: Add new page to example site --- .../content/test-product/cards/_index.md | 5 + .../content/test-product/cards/permitted.md | 93 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 exampleSite/content/test-product/cards/_index.md create mode 100644 exampleSite/content/test-product/cards/permitted.md diff --git a/exampleSite/content/test-product/cards/_index.md b/exampleSite/content/test-product/cards/_index.md new file mode 100644 index 0000000..7346afc --- /dev/null +++ b/exampleSite/content/test-product/cards/_index.md @@ -0,0 +1,5 @@ +--- +description: Card usage +title: Cards +weight: 300 +--- \ No newline at end of file diff --git a/exampleSite/content/test-product/cards/permitted.md b/exampleSite/content/test-product/cards/permitted.md new file mode 100644 index 0000000..ade7eff --- /dev/null +++ b/exampleSite/content/test-product/cards/permitted.md @@ -0,0 +1,93 @@ +--- +description: Card usage +title: Usage +weight: 100 +--- +## General usage +The structure of a card looks like the following: +```plaintext + + + SOME CONTENT icon="SOME_LUCIDE_ICON"><\card> + ... + SOME CONTENT<\card> + + +``` + +and will render as the following: +{{}} + {{}} + {{}} + SOME CONTENT + {{}} + {{}} +{{}} + +### Params +To support customization, there are also some params you can add to the shortcode `` such as `title`, `titleUrl`, `icon`, `brandIcon`, `isFeatured`, `isFullSize`. + +* `title` - Title of the card. +
+* `titleUrl` - URL for the card. +
+* `icon` - Custom icon using lucide icons. +
+* `brandIcon` - Custom icon using image from `nginx-hugo-theme/static/images/icons`. + * Usage: `` +
+* `isFeatured` - Boolean indicating whether or not the card should be the first one and full size. By default, false. + * Usage: `` +
+* `isFullSize` - Boolean indicating whether or not the card should be full size. By default, cards are half sized. + * Usage: `` + +For the ``, there are some params you can add such as `title`, `isFeaturedSection`, and `showAsCards`. +* `title` - Title of the section. +
+* `isFeaturedSection` - Boolean indicating whether or not the section is a featured one - will discuss later down the page. By default, false. +
+* `showAsCards` - Boolean indicating whether or not the cards in the section should appear with borders. By default, false. + +### Additional Information +While it may come immediate, you can't use a `` 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. + +## Show as cards +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 ``. The usage as seen below: +```plaintext + + + SOME CONTENT icon="SOME_LUCIDE_ICON"><\card> + ... + SOME CONTENT<\card> + + +``` +and will render as the following: +{{}} + {{}} + {{}} + SOME CONTENT + {{}} + {{}} +{{}} + +## Featured Cards +Denoted by the param `isFeaturedSection` in the shortcode ``, this block of cards can contain only up to three cards. + +{{}} + {{}} + {{}} + All shortcodes in one page. + {{}} + {{}} + Examples for call-out shortcode + {{}} + {{}} + Examples for codeblock shortcode + {{}} + {{}} + Examples for codeblock shortcode + {{}} + {{}} +{{}} From 199d4269a654de733843bf82d657cad95d94c1c4 Mon Sep 17 00:00:00 2001 From: Lam Nguyen Date: Mon, 7 Jul 2025 15:16:08 -0700 Subject: [PATCH 3/4] Cards: Add tests --- assets/css/v2/style.css | 5 + .../content/test-product/cards/permitted.md | 95 ++++++++++++------- layouts/shortcodes/card-layout.html | 2 +- layouts/shortcodes/card-section.html | 6 +- layouts/shortcodes/card.html | 6 +- tests/src/cards.spec.js | 71 ++++++++++++++ 6 files changed, 144 insertions(+), 41 deletions(-) create mode 100644 tests/src/cards.spec.js diff --git a/assets/css/v2/style.css b/assets/css/v2/style.css index aafdb3d..6ab8881 100644 --- a/assets/css/v2/style.css +++ b/assets/css/v2/style.css @@ -1483,6 +1483,11 @@ h6:has(a):hover { flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; + order: 2; + + &.featured-card { + order: 1; + } .card-header { display: flex; diff --git a/exampleSite/content/test-product/cards/permitted.md b/exampleSite/content/test-product/cards/permitted.md index ade7eff..ac109a2 100644 --- a/exampleSite/content/test-product/cards/permitted.md +++ b/exampleSite/content/test-product/cards/permitted.md @@ -16,16 +16,18 @@ The structure of a card looks like the following: ``` and will render as the following: -{{}} - {{}} - {{}} - SOME CONTENT - {{}} - {{}} -{{}} +
+ {{}} + {{}} + {{}} + SOME CONTENT + {{}} + {{}} + {{}} +
### Params -To support customization, there are also some params you can add to the shortcode `` such as `title`, `titleUrl`, `icon`, `brandIcon`, `isFeatured`, `isFullSize`. +To support customization, there are also some params you can add to the shortcode `` such as `title`, `titleUrl`, `icon`, `brandIcon`, `isFeaturedCard`, `isFullSize`. * `title` - Title of the card.
@@ -36,8 +38,8 @@ To support customization, there are also some params you can add to the shortcod * `brandIcon` - Custom icon using image from `nginx-hugo-theme/static/images/icons`. * Usage: ``
-* `isFeatured` - Boolean indicating whether or not the card should be the first one and full size. By default, false. - * Usage: `` +* `isFeaturedCard` - Boolean indicating whether or not the card should be the first one and full size. By default, false. + * Usage: ``
* `isFullSize` - Boolean indicating whether or not the card should be full size. By default, cards are half sized. * Usage: `` @@ -64,30 +66,55 @@ As you can see from the above example in 'General usage', it renders as a plain
``` and will render as the following: -{{}} - {{}} - {{}} - SOME CONTENT - {{}} - {{}} -{{}} +
+ {{}} + {{}} + {{}} + SOME CONTENT + {{}} + {{}} + {{}} +
-## Featured Cards +## Featured Section Denoted by the param `isFeaturedSection` in the shortcode ``, this block of cards can contain only up to three cards. -{{}} - {{}} - {{}} - All shortcodes in one page. - {{}} - {{}} - Examples for call-out shortcode - {{}} - {{}} - Examples for codeblock shortcode - {{}} - {{}} - Examples for codeblock shortcode - {{}} - {{}} -{{}} +
+ {{}} + {{}} + {{}} + All shortcodes in one page. + {{}} + {{}} + Examples for call-out shortcode + {{}} + {{}} + Examples for codeblock shortcode + {{}} + {{}} + Examples for codeblock shortcode + {{}} + {{}} + {{}} +
+ +## Featured Card +Denoted by the param `isFeaturedCard` in the shortcode ``, this will push the card up to very top of the section and make it full length. +
+ {{}} + {{}} + {{}} + All shortcodes in one page. + {{}} + {{}} + Examples for call-out shortcode + {{}} + {{}} + Examples for codeblock shortcode + {{}} + {{}} + Examples for codeblock shortcode + {{}} + {{}} + {{}} +
\ No newline at end of file diff --git a/layouts/shortcodes/card-layout.html b/layouts/shortcodes/card-layout.html index e533716..57d8347 100644 --- a/layouts/shortcodes/card-layout.html +++ b/layouts/shortcodes/card-layout.html @@ -1,2 +1,2 @@ -
{{- .Inner | markdownify -}}
+
{{- .Inner | markdownify -}}
diff --git a/layouts/shortcodes/card-section.html b/layouts/shortcodes/card-section.html index 52335d2..fb0534f 100644 --- a/layouts/shortcodes/card-section.html +++ b/layouts/shortcodes/card-section.html @@ -22,12 +22,12 @@ {{- /* Validate that the parent is card-layout */ -}} {{ if eq .Parent.Name "card-layout"}} -
+
{{- if $title -}} {{- $title -}} -
{{- .Inner -}}
+
{{- .Inner -}}
{{ else }} -
{{ .Inner }}
+
{{ .Inner }}
{{ end }}
{{ else }} diff --git a/layouts/shortcodes/card.html b/layouts/shortcodes/card.html index 5b5b2dd..c992506 100644 --- a/layouts/shortcodes/card.html +++ b/layouts/shortcodes/card.html @@ -2,12 +2,12 @@ {{- $titleUrl := .Get "titleUrl" | default "/" -}} {{- $icon := .Get "icon" | default "book-open" -}} {{- $brandIcon := .Get "brandIcon" -}} -{{- $isFeaturedParam := .Get "isFeatured" | default "false" }} +{{- $isFeaturedParam := .Get "isFeaturedCard" | default "false" }} {{- $isFullSizeParam := .Get "isFullSize" | default "false" -}} {{- /* Validate the parameter strictly */ -}} {{- if not (in (slice "true" "false") $isFeaturedParam) -}} - {{- warnf "The '' Shortcode parameter 'isFeatured' must be 'true' or 'false', but got: '%s'. This will now default to 'false'" $isFeaturedParam -}} + {{- warnf "The '' Shortcode parameter 'isFeaturedCard' must be 'true' or 'false', but got: '%s'. This will now default to 'false'" $isFeaturedParam -}} {{- end -}} {{- $isFeatured := cond (eq $isFeaturedParam "true") "true" "false" -}} @@ -26,7 +26,7 @@ {{- /* Validate that the parent is card-section and under 3 cards */ -}} {{- if (eq .Parent.Name "card-section") -}} -
+
{{- if $title -}}
{{- if $brandIcon -}} diff --git a/tests/src/cards.spec.js b/tests/src/cards.spec.js new file mode 100644 index 0000000..3f40eb3 --- /dev/null +++ b/tests/src/cards.spec.js @@ -0,0 +1,71 @@ +import { expect, test } from '@playwright/test'; + +test.describe('Testing for cards shortcode', () => { + test.beforeEach(async ({ page }) => { + const cardsUrl = 'test-product/cards/permitted'; + await page.goto(`/${cardsUrl}`); + }); + + test('should test basic section', async ({ page }) => { + const section = await page.locator('data-testid=cards-test__basic'); + const basicCode = await section.locator('data-testid=card-section-content'); + const showAsCardCode = await section.locator( + 'data-testid=card-section-content__card-grid' + ); + + expect(await basicCode.count()).toBeTruthy(); + expect(await showAsCardCode.count()).toBe(0); + }); + + test('should test showAsCard section', async ({ page }) => { + const section = await page.locator('data-testid=cards-test__showAsCards'); + const showAsCardCode = await section.locator( + 'data-testid=card-section-content__card-grid' + ); + + expect(await showAsCardCode.count()).toBeTruthy(); + }); + + test('should test featured section', async ({ page }) => { + const section = await page.locator( + 'data-testid=cards-test__featuredSection' + ); + const featuredSection = await section.locator( + 'data-testid=card-section__featured-section' + ); + const cards = await ( + await featuredSection.locator('data-testid=card') + ).all(); + + // Test featured section exists AND there are less than or equal to 3 cards. + expect(await featuredSection.count()).toBeTruthy(); + const visibleCards = []; + for (const card of cards) { + if (await card.isVisible()) { + visibleCards.push(card); + } + } + expect(visibleCards.length).toBeLessThanOrEqual(3); + }); + + test('should test featured card', async ({ page }) => { + const section = await page.locator('data-testid=cards-test__featuredCard'); + const featuredCard = await section.locator( + 'data-testid=card__featured-card' + ); + const cards = await (await section.locator('data-testid=card')).all(); + const featuredCardOrder = await featuredCard.evaluate((el) => { + return window.getComputedStyle(el).getPropertyValue('order'); + }); + + // Test featured card exist AND is the first one in the order + expect(await featuredCard.count()).toBeTruthy(); + expect(featuredCardOrder).toBe('1'); + for (const card of cards) { + const order = await card.evaluate((el) => { + return window.getComputedStyle(el).getPropertyValue('order'); + }); + expect(order).toBe('2'); + } + }); +}); From 5a52a5fff2bf019aaa2cd07a06314404ef6c113a Mon Sep 17 00:00:00 2001 From: Lam Nguyen Date: Tue, 8 Jul 2025 08:29:30 -0700 Subject: [PATCH 4/4] Cards: Small tweaks --- assets/css/v2/style.css | 6 +++--- layouts/shortcodes/card.html | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/css/v2/style.css b/assets/css/v2/style.css index 6ab8881..c22d28a 100644 --- a/assets/css/v2/style.css +++ b/assets/css/v2/style.css @@ -880,7 +880,7 @@ nav { [data-grid="last-third"] { grid-column: 1; } - [data-grid="halve"] { + [data-grid="half"] { grid-column: 1; } @@ -909,7 +909,7 @@ nav { grid-column: 8 / -1; } - [data-grid="halve"] { + [data-grid="half"] { grid-column: span 6; } } @@ -1502,7 +1502,7 @@ h6:has(a):hover { h2 { padding: 0; margin: 0; - font-size: 1rem; + font-size: var(--font-step-1); } } } diff --git a/layouts/shortcodes/card.html b/layouts/shortcodes/card.html index c992506..403ef21 100644 --- a/layouts/shortcodes/card.html +++ b/layouts/shortcodes/card.html @@ -21,7 +21,7 @@ {{ if or (eq $isFullSize "true") (eq $isFeatured "true") }} {{ $dataGrid = "wide"}} {{ else }} - {{ $dataGrid = "halve"}} + {{ $dataGrid = "half"}} {{ end }} {{- /* Validate that the parent is card-section and under 3 cards */ -}}