diff --git a/assets/css/v2/style.css b/assets/css/v2/style.css index b63251c2..727c7f34 100644 --- a/assets/css/v2/style.css +++ b/assets/css/v2/style.css @@ -1482,13 +1482,21 @@ h6:has(a):hover { &.featured-section { /* Hide all the cards past 3 if it is a featured card section */ - .card-section-content > *:nth-child(n + 4 of div.card) { + .card-section-content > *:nth-child(n + 4 of a.card) { display: none; } } } .card { + color: oklch(var(--color-foreground)); + text-decoration: none; + order: 2; + + &.featured-card { + order: 1; + } + .card-container { display: flex; flex-direction: column; @@ -1500,6 +1508,7 @@ h6:has(a):hover { flex-direction: row; gap: 1rem; align-items: center; + color: oklch(var(--color-brand)); .card-brand-icon { height: 20px; diff --git a/exampleSite/content/test-product/cards/_index.md b/exampleSite/content/test-product/cards/_index.md new file mode 100644 index 00000000..7346afc8 --- /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 00000000..933ea962 --- /dev/null +++ b/exampleSite/content/test-product/cards/permitted.md @@ -0,0 +1,120 @@ +--- +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`, `isFeaturedCard`, `isFullSize`. + +* `title` (required) - Title of the card. +
+* `titleUrl` (optional) - URL for the card. +
+* `icon` (optional) - Custom icon using lucide icons. +
+* `brandIcon` (optional) - Custom icon using image from `nginx-hugo-theme/static/images/icons`. + * Usage: `` +
+* `isFeaturedCard` (optional) - Boolean indicating whether or not the card should be the first one and full size. By default, false. + * Usage: `` +
+* `isFullSize` (optional) - 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` (optional) - Boolean indicating whether or not the section is a featured one - will discuss later down the page. By default, false. +
+* `showAsCards` (optional) - 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 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 + {{}} + {{}} + {{}} +
+ +## 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 e5337167..57d83472 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 77633f00..26478172 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 af4dfe49..490be379 100644 --- a/layouts/shortcodes/card.html +++ b/layouts/shortcodes/card.html @@ -2,13 +2,13 @@ {{- $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" -}} {{- $isLandingParam := .Get "isLanding" | 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" -}} @@ -34,7 +34,7 @@ {{- /* Validate that the parent is card-section and under 3 cards */ -}} {{- if (eq .Parent.Name "card-section") -}} - +
{{- if $title -}}
diff --git a/tests/src/cards.spec.js b/tests/src/cards.spec.js new file mode 100644 index 00000000..3f40eb38 --- /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'); + } + }); +});