From 96a14ffb73e693878fdca88c0d6ce55e68482397 Mon Sep 17 00:00:00 2001 From: Michaela Robosova Date: Thu, 21 Nov 2024 11:28:11 +0100 Subject: [PATCH 1/3] Add cards QA page --- docs/pages/qa/README.md | 1 + docs/pages/qa/cards.vue | 277 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+) create mode 100644 docs/pages/qa/README.md create mode 100644 docs/pages/qa/cards.vue diff --git a/docs/pages/qa/README.md b/docs/pages/qa/README.md new file mode 100644 index 000000000..4fe9d6b2b --- /dev/null +++ b/docs/pages/qa/README.md @@ -0,0 +1 @@ +Contains pages of rather private character that serve for manual preview of components. Accessible on url /qa/... diff --git a/docs/pages/qa/cards.vue b/docs/pages/qa/cards.vue new file mode 100644 index 000000000..53cd1706d --- /dev/null +++ b/docs/pages/qa/cards.vue @@ -0,0 +1,277 @@ + + + + + + + \ No newline at end of file From f19114a8c2823e58ffd9e2826af6cb6b8256f2dc Mon Sep 17 00:00:00 2001 From: Michaela Robosova Date: Tue, 19 Nov 2024 19:29:38 +0100 Subject: [PATCH 2/3] Improve content tolerance Fixes thumbnail overflow problems in some layouts and makes styles more robust and better organized overall. --- docs/pages/qa/cards.vue | 8 +- lib/cards/KCard.vue | 490 +++++++++++++++--------------- lib/cards/__tests__/KCard.spec.js | 73 ++--- 3 files changed, 272 insertions(+), 299 deletions(-) diff --git a/docs/pages/qa/cards.vue b/docs/pages/qa/cards.vue index 53cd1706d..f1cb46683 100644 --- a/docs/pages/qa/cards.vue +++ b/docs/pages/qa/cards.vue @@ -168,14 +168,14 @@ :headingLevel="3" orientation="horizontal" :thumbnailSrc="require('../../assets/hummingbird-large-cc-by-sa-4.jpg')" - title="First card when link" + title="First card" /> @@ -187,13 +187,13 @@ :headingLevel="3" orientation="horizontal" :thumbnailSrc="require('../../assets/hummingbird-large-cc-by-sa-4.jpg')" - title="First card when not link" + title="First card" /> diff --git a/lib/cards/KCard.vue b/lib/cards/KCard.vue index fee4137ec..a3f9581d4 100644 --- a/lib/cards/KCard.vue +++ b/lib/cards/KCard.vue @@ -17,7 +17,9 @@ :class="['card-area', ...cardAreaClasses ]" :style="{ backgroundColor: $themeTokens.surface }" > - - - {{ title }} - - - - - - - - - - -
- - - + + + + +
+ + +
+ +
+ + +
+
+ +
- - - -
-
- - -
-
- - + + + + + + +
+
in KImg with z-index 1 should cover the placeholder after loaded */ - display: flex; - align-items: center; - justify-content: center; - height: 100%; - } - .title { display: inline-block; // allows title placeholder in the skeleton card width: 100%; // allows title placeholder in the skeleton card @@ -634,57 +603,94 @@ outline: none; // the focus ring is moved to the whole
  • } - /************* Manage spaces *************/ + // Because the title and the areas above and below it + // are grouped together in all layoyts, abstract them + // into one whole here. Simplifies common spacing + // styles as well as layout-specific styles. + .around-title { + display: flex; + flex-direction: column; + + .heading { + order: 2; + } + + .above-title { + order: 1; + } + + .below-title { + order: 3; + } + } + + /************* Spacing *************/ + // first reset + .around-title, .heading, .above-title, .below-title, .footer { padding: 0; - margin-top: 0; - margin-right: $spacer; - margin-bottom: calc(#{$spacer} / 2); - margin-left: $spacer; + margin: 0; } - // when the 'aboveTitle' area is present, - // apply top margin to it and also set smaller - // margin between the area and the heading... - .with-above-title { - .above-title { - margin-top: $spacer; - margin-bottom: calc(#{$spacer} / 2); - } + /* stylelint-disable no-duplicate-selectors */ + .around-title { + padding: $spacer; } + /* stylelint-enable no-duplicate-selectors */ - // ...and when the 'aboveTitle' area is not present, - // instead apply the top margin to the heading - .without-above-title { - .heading { - margin-top: $spacer; - } + .footer { + padding: 0 $spacer $spacer $spacer; } - // if there's the 'belowTitle' area present, - // override the heading's margin to smaller one - .with-below-title { - .heading { - margin-bottom: calc(#{$spacer} / 2); - } + .above-title { + padding-bottom: calc(#{$spacer} / 2); } + .below-title { + padding-top: calc(#{$spacer} / 2); + } + + /************* Thumbnail **************/ + /* stylelint-disable no-duplicate-selectors */ - .footer { - margin-top: auto; // push footer to the bottom + .thumbnail { + position: relative; /* for absolute positioning of .thumbnail-placeholder */ } /* stylelint-enable no-duplicate-selectors */ + .thumbnail-placeholder { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 0; /* in KImg with z-index 1 should cover the placeholder after loaded */ + display: flex; + align-items: center; + justify-content: center; + height: 100%; + } + /************* Layout-specific styles *************/ .vertical-with-large-thumbnail { + .upper-card-area { + display: flex; + flex-direction: column; + } + .thumbnail { + order: 1; height: 180px; } + + .around-title { + order: 2; + } } .vertical-with-small-thumbnail { @@ -698,104 +704,92 @@ } } - .horizontal-with-large-thumbnail { - $thumbnail-width: 40%; + .horizontal-with-small-thumbnail { + .upper-card-area { + display: flex; + flex-direction: row; + align-items: flex-start; + } .thumbnail { - position: absolute; - width: $thumbnail-width; - height: 100%; + width: 30%; + margin-top: $spacer; + margin-bottom: $spacer; } - .heading, - .above-title, - .below-title, - .footer { - width: calc(100% - #{$thumbnail-width} - 2 * #{$spacer}); + .around-title { + width: 70%; } &.thumbnail-align-left { - align-items: flex-end; - .thumbnail { - left: 0; + order: 1; + margin-left: $spacer; + } + + .around-title { + order: 2; } } &.thumbnail-align-right { - align-items: flex-start; - .thumbnail { - right: 0; + order: 2; + margin-right: $spacer; + } + + .around-title { + order: 1; } } } - .horizontal-with-small-thumbnail { - $thumbnail-width: null; - - /* - Coordinates space taken by the thumbnail area and the content area - next to it more intelligently in browsers that support `clamp()` by: - - - Instead of defining 'width', 'min-width', and 'max-width' separately, - `clamp()` is used with the goal to have the actual thumbnail width - saved in the single `$thumbnail-width` value. - - - The `$thumbnail-width` value is then referenced when calculating - the remaining space for the content area, ensuring the precise - distribution of space. - - Resolves some issues related to unprecise calculations, most importantly - this removes the area of empty space between the thumbnail and content areas - in some card's sizes, wasting space that can be used for card's textual content. - */ - @mixin clamp-with-fallback($min, $preferred, $max) { - // fallback for browsers that don't support 'clamp()' - $thumbnail-width: $preferred; - - width: $thumbnail-width; - min-width: $min; - max-width: $max; + .horizontal-with-large-thumbnail { + // override few styles from .horizontal-with-small-thumbnail + // to stretch the thumbnail to the full height of the card - // clamp(1px, 1%, 1px) only used for testing support - @supports (width: clamp(1px, 1%, 1px)) { - $thumbnail-width: clamp(#{$min}, #{$preferred}, #{$max}); + /* stylelint-disable scss/at-extend-no-missing-placeholder */ + @extend .horizontal-with-small-thumbnail; + /* stylelint-enable scss/at-extend-no-missing-placeholder */ - width: $thumbnail-width; - } + &.card-area { + position: relative; /* for absolute positioning of .thumbnail */ } .thumbnail { - @include clamp-with-fallback(100px, 35%, 120px); - position: absolute; - top: $spacer; - } - - .heading, - .above-title, - .below-title { - width: calc(100% - #{$thumbnail-width} - 3 * #{$spacer}); + width: 40%; + height: 100%; + margin-top: 0; + margin-bottom: 0; } + .around-title, .footer { - width: calc(100% - 2 * #{$spacer}); + width: 60%; } &.thumbnail-align-left { - align-items: flex-end; - .thumbnail { - left: $spacer; + left: 0; + margin-left: 0; + } + + .around-title, + .footer { + margin-left: auto; } } &.thumbnail-align-right { - align-items: flex-start; - .thumbnail { - right: $spacer; + right: 0; + margin-right: 0; + } + + .around-title, + .footer { + margin-right: auto; } } } diff --git a/lib/cards/__tests__/KCard.spec.js b/lib/cards/__tests__/KCard.spec.js index 978fd9a11..a7041dddf 100644 --- a/lib/cards/__tests__/KCard.spec.js +++ b/lib/cards/__tests__/KCard.spec.js @@ -30,41 +30,31 @@ describe('KCard', () => { // Check for: // - // li - // |-- div - // |--span (visually hidden title text) - // |-- h[2-6] - // |--a (with aria-labelledby pointing to the span above) - // |-- span (with aria-hidden) + //
  • + // |-- with visually hidden title text + // |-- + // |-- with aria-labelledby pointing to the span with the visually hidden title text + // |-- with aria-hidden // const el1 = wrapper.find(':first-child'); expect(el1.exists()).toBe(true); expect(el1.element.tagName.toLowerCase()).toBe('li'); - const el2 = wrapper.find('li > :first-child'); + const el2 = wrapper.find('li .visuallyhidden'); expect(el2.exists()).toBe(true); - expect(el2.element.tagName.toLowerCase()).toBe('div'); + expect(el2.text()).toBe('Test Title'); + const spanId = el2.attributes('id'); - const el3 = wrapper.find('li > div > :nth-child(1)'); + const el3 = wrapper.find('li h4'); expect(el3.exists()).toBe(true); - expect(el3.element.tagName.toLowerCase()).toBe('span'); - expect(el3.classes()).toContain('visuallyhidden'); - expect(el3.text()).toBe('Test Title'); - const spanId = el3.attributes('id'); - const el4 = wrapper.find('li > div > :nth-child(2)'); + const el4 = wrapper.find(`li h4 [aria-labelledby="${spanId}"]`); expect(el4.exists()).toBe(true); - expect(el4.element.tagName.toLowerCase()).toBe('h4'); + expect(el4.element.tagName.toLowerCase()).toBe('a'); - const el5 = wrapper.find(`li > div > h4 > :first-child`); + const el5 = wrapper.find(`li h4 [aria-labelledby="${spanId}"] > :first-child`); expect(el5.exists()).toBe(true); - expect(el5.element.tagName.toLowerCase()).toBe('a'); - expect(el5.attributes('aria-labelledby')).toBe(spanId); - - const el6 = wrapper.find(`li > div > h4 > a > :first-child`); - expect(el6.exists()).toBe(true); - expect(el6.element.tagName.toLowerCase()).toBe('span'); - expect(el6.attributes('aria-hidden')).toBe('true'); + expect(el5.attributes('aria-hidden')).toBe('true'); }); it(`renders the correct accessibility structure when card is not link`, () => { @@ -74,42 +64,31 @@ describe('KCard', () => { // Check for: // - // li - // |-- div - // |--span (visually hidden title text) - // |-- h[2-6] - // |--span (focusable and with aria-labelledby pointing to the span above) - // |-- span (with aria-hidden) + //
  • + // |-- with visually hidden title text + // |-- + // |-- with aria-labelledby pointing to the span with the visually hidden title text + // |-- with aria-hidden // const el1 = wrapper.find(':first-child'); expect(el1.exists()).toBe(true); expect(el1.element.tagName.toLowerCase()).toBe('li'); - const el2 = wrapper.find('li > :first-child'); + const el2 = wrapper.find('li .visuallyhidden'); expect(el2.exists()).toBe(true); - expect(el2.element.tagName.toLowerCase()).toBe('div'); + expect(el2.text()).toBe('Test Title'); + const spanId = el2.attributes('id'); - const el3 = wrapper.find('li > div > :nth-child(1)'); + const el3 = wrapper.find('li h4'); expect(el3.exists()).toBe(true); - expect(el3.element.tagName.toLowerCase()).toBe('span'); - expect(el3.classes()).toContain('visuallyhidden'); - expect(el3.text()).toBe('Test Title'); - const spanId = el3.attributes('id'); - const el4 = wrapper.find('li > div > :nth-child(2)'); + const el4 = wrapper.find(`li h4 [aria-labelledby="${spanId}"]`); expect(el4.exists()).toBe(true); - expect(el4.element.tagName.toLowerCase()).toBe('h4'); + expect(el4.element.tagName.toLowerCase()).toBe('span'); - const el5 = wrapper.find(`li > div > h4 > :first-child`); + const el5 = wrapper.find(`li h4 [aria-labelledby="${spanId}"] > :first-child`); expect(el5.exists()).toBe(true); - expect(el5.element.tagName.toLowerCase()).toBe('span'); - expect(el5.attributes('aria-labelledby')).toBe(spanId); - expect(el5.attributes('tabindex')).toBe('0'); - - const el6 = wrapper.find(`li > div > h4 > span > :first-child`); - expect(el6.exists()).toBe(true); - expect(el6.element.tagName.toLowerCase()).toBe('span'); - expect(el6.attributes('aria-hidden')).toBe('true'); + expect(el5.attributes('aria-hidden')).toBe('true'); }); describe('on long click', () => { From 263bd7ec5e5da3a60a68f9f1ae4ec09b886c0eee Mon Sep 17 00:00:00 2001 From: Michaela Robosova Date: Wed, 4 Dec 2024 17:16:13 +0100 Subject: [PATCH 3/3] Move cards qa page --- docs/pages/{qa => playground}/cards.vue | 0 docs/pages/qa/README.md | 1 - 2 files changed, 1 deletion(-) rename docs/pages/{qa => playground}/cards.vue (100%) delete mode 100644 docs/pages/qa/README.md diff --git a/docs/pages/qa/cards.vue b/docs/pages/playground/cards.vue similarity index 100% rename from docs/pages/qa/cards.vue rename to docs/pages/playground/cards.vue diff --git a/docs/pages/qa/README.md b/docs/pages/qa/README.md deleted file mode 100644 index 4fe9d6b2b..000000000 --- a/docs/pages/qa/README.md +++ /dev/null @@ -1 +0,0 @@ -Contains pages of rather private character that serve for manual preview of components. Accessible on url /qa/...