Skip to content

Commit 00de731

Browse files
author
Dobromir Hristov
committed
Merge remote-tracking branch 'public-docc/main' into dhristov/r97714707-introduce-card-component
2 parents e5dbbac + 2abc274 commit 00de731

File tree

6 files changed

+73
-29
lines changed

6 files changed

+73
-29
lines changed

src/components/AdjustableSidebarWidth.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
class="aside"
2727
ref="aside"
2828
:aria-hidden="hiddenOnLarge ? 'true': null"
29-
@transitionstart="trackTransitionStart"
30-
@transitionend="trackTransitionEnd"
29+
@transitionstart.self="trackTransitionStart"
30+
@transitionend.self="trackTransitionEnd"
3131
>
3232
<slot
3333
name="aside"

src/components/ImageAsset.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
v-if="fallbackImageSrcSet"
1414
class="fallback"
1515
title="Image failed to load"
16+
decoding="async"
1617
:alt="alt"
1718
:srcset="fallbackImageSrcSet"
1819
/>
@@ -31,6 +32,8 @@
3132
v-if="prefersDark && darkVariantAttributes"
3233
v-bind="darkVariantAttributes"
3334
ref="img"
35+
decoding="async"
36+
:loading="loading"
3437
:alt="alt"
3538
:width="darkVariantAttributes.width || optimalWidth"
3639
:height="(darkVariantAttributes.width || optimalWidth) ? 'auto' : null"
@@ -43,6 +46,8 @@
4346
v-else
4447
v-bind="defaultAttributes"
4548
ref="img"
49+
decoding="async"
50+
:loading="loading"
4651
:alt="alt"
4752
:width="defaultAttributes.width || optimalWidth"
4853
:height="(defaultAttributes.width || optimalWidth) ? 'auto' : null"
@@ -129,6 +134,10 @@ export default {
129134
type: Array,
130135
required: true,
131136
},
137+
loading: {
138+
type: String,
139+
default: 'lazy',
140+
},
132141
},
133142
methods: {
134143
handleImageLoadError() {

src/components/Navigator/NavigatorCard.vue

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -889,16 +889,17 @@ export default {
889889
await waitFrames(1);
890890
if (!this.$refs.scroller) return;
891891
// if we are filtering, it makes more sense to scroll to top of list
892-
if (this.hasFilter) {
892+
if (this.hasFilter && !this.deprecatedHidden) {
893893
this.$refs.scroller.scrollToItem(0);
894894
return;
895895
}
896896
// check if the current element is visible and needs scrolling into
897897
const element = document.getElementById(this.activeUID);
898-
// check if item is inside scroller
899-
if (this.getChildPositionInScroller(element) === 0) return;
900-
// find the index of the current active UID in the newly added nodes
898+
// check if there is such an item AND the item is inside scroller area
899+
if (element && this.getChildPositionInScroller(element) === 0) return;
900+
// find the index of the current active UID in the nodes to render
901901
const index = this.nodesToRender.findIndex(child => child.uid === this.activeUID);
902+
if (index === -1) return;
902903
// check if the element is visible
903904
// call the scroll method on the `scroller` component.
904905
this.$refs.scroller.scrollToItem(index);

tests/unit/components/Asset.spec.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ describe('Asset', () => {
185185
expect(asset.exists()).toBe(true);
186186
expect(asset.props()).toEqual({
187187
alt: image.alt,
188+
loading: "lazy",
188189
variants: image.variants,
189190
});
190191
});
@@ -194,6 +195,7 @@ describe('Asset', () => {
194195
const asset = wrapper.find(ImageAsset);
195196
expect(asset.props()).toEqual({
196197
alt: image.alt,
198+
loading: "lazy",
197199
variants: image.variants,
198200
});
199201
});

tests/unit/components/ImageAsset.spec.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ describe('ImageAsset', () => {
5454
expect(image.attributes('width')).toBe('1202');
5555
expect(image.attributes('height')).toBe('auto');
5656
expect(image.attributes('alt')).toBe(alt);
57+
expect(image.attributes('decoding')).toBe('async');
5758
});
5859

5960
it('renders an image that has one variant with no appearance trait', () => {
@@ -85,6 +86,7 @@ describe('ImageAsset', () => {
8586
expect(image.attributes('width')).toBe('300');
8687
expect(image.attributes('height')).toBe('auto');
8788
expect(image.attributes('alt')).toBe('');
89+
expect(image.attributes('decoding')).toBe('async');
8890
});
8991

9092
it('renders an image that has two light variants', () => {
@@ -128,6 +130,7 @@ describe('ImageAsset', () => {
128130
expect(image.attributes('srcset')).toBe(`${url2x} 2x, ${url3x} 3x`);
129131
expect(image.attributes('width')).toBe('1202');
130132
expect(image.attributes('height')).toBe('auto');
133+
expect(image.attributes('decoding')).toBe('async');
131134
});
132135

133136
it('renders an image that has two dark variants', () => {

tests/unit/components/Navigator/NavigatorCard.spec.js

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,16 @@ function mergeSessionState(state) {
171171
};
172172
}
173173

174+
function attachDivWithID(id) {
175+
if (document.getElementById(id)) return;
176+
const div = document.createElement('DIV');
177+
div.id = id;
178+
document.body.appendChild(div);
179+
}
180+
174181
describe('NavigatorCard', () => {
175182
beforeEach(() => {
183+
document.body.innerHTML = '';
176184
jest.clearAllMocks();
177185
// mock the position helper function, as its too difficult to mock the boundingClientRects
178186
getChildPositionInScroller = jest.spyOn(NavigatorCard.methods, 'getChildPositionInScroller')
@@ -929,6 +937,7 @@ describe('NavigatorCard', () => {
929937
});
930938

931939
it('allows filtering the items, opening all items, that have matches in children', async () => {
940+
attachDivWithID(root0Child0.uid);
932941
const wrapper = createWrapper();
933942
await flushPromises();
934943
// item is not scrolled to
@@ -959,6 +968,7 @@ describe('NavigatorCard', () => {
959968
root0Child1GrandChild0,
960969
root1,
961970
];
971+
attachDivWithID(root0Child0.uid);
962972

963973
const wrapper = createWrapper({
964974
propsData: {
@@ -980,6 +990,7 @@ describe('NavigatorCard', () => {
980990
});
981991

982992
it('renders all the children of a directly matched parent', async () => {
993+
attachDivWithID(root0Child0.uid);
983994
const wrapper = createWrapper();
984995
const filter = wrapper.find(FilterInput);
985996
await flushPromises();
@@ -1007,6 +1018,7 @@ describe('NavigatorCard', () => {
10071018
});
10081019

10091020
it('allows filtering the items using Tags, opening all items, that have matches in children', async () => {
1021+
attachDivWithID(root0Child0.uid);
10101022
const wrapper = createWrapper();
10111023
await flushPromises();
10121024
const filter = wrapper.find(FilterInput);
@@ -1024,6 +1036,7 @@ describe('NavigatorCard', () => {
10241036
});
10251037

10261038
it('aliases `project` to `tutorial`, when filtering using tags', async () => {
1039+
attachDivWithID(root0Child0.uid);
10271040
const wrapper = createWrapper();
10281041
const filter = wrapper.find(FilterInput);
10291042
await flushPromises();
@@ -1038,6 +1051,7 @@ describe('NavigatorCard', () => {
10381051
});
10391052

10401053
it('allows filtering the items with filter and Tags, opening all items, that have matches in children', async () => {
1054+
attachDivWithID(root0Child0.uid);
10411055
const wrapper = createWrapper();
10421056
const filter = wrapper.find(FilterInput);
10431057
await flushPromises();
@@ -1742,10 +1756,14 @@ describe('NavigatorCard', () => {
17421756
describe('navigating', () => {
17431757
it('changes the open item, when navigating across pages, keeping the previously open items', async () => {
17441758
// simulate navigating to the bottom most item.
1759+
attachDivWithID(root0Child0.uid);
17451760
const wrapper = createWrapper();
17461761
await flushPromises();
1762+
// item is in viewport, no need to scroll to it
1763+
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
17471764
// simulate the new item is below the fold
17481765
getChildPositionInScroller.mockReturnValueOnce(1);
1766+
attachDivWithID(root0Child1GrandChild0.uid);
17491767
wrapper.setProps({
17501768
activePath: [
17511769
root0.path,
@@ -1754,6 +1772,7 @@ describe('NavigatorCard', () => {
17541772
],
17551773
});
17561774
await flushPromises();
1775+
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
17571776
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(3);
17581777
// assert all are open
17591778
const all = wrapper.findAll(NavigatorCardItem);
@@ -1786,13 +1805,15 @@ describe('NavigatorCard', () => {
17861805
// simulate the new item is above the scrollarea
17871806
getChildPositionInScroller.mockReturnValueOnce(-1);
17881807
// navigate to the top level sibling
1808+
attachDivWithID(root1.uid);
17891809
wrapper.setProps({
17901810
activePath: [
17911811
root1.path,
17921812
],
17931813
});
17941814
await flushPromises();
17951815
// assert it scrolls to the item
1816+
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(2);
17961817
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(4);
17971818
// assert items are still open
17981819
expect(all.at(0).props()).toMatchObject({
@@ -1828,13 +1849,18 @@ describe('NavigatorCard', () => {
18281849
});
18291850

18301851
it('tracks current open item, from clicking child items, handling duplicate router changes on the way', async () => {
1852+
attachDivWithID(root0Child0.uid);
18311853
const wrapper = createWrapper();
18321854
await flushPromises();
1855+
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
18331856
let allItems = wrapper.findAll(NavigatorCardItem);
18341857
const targetChild = allItems.at(2);
18351858
expect(targetChild.props('item')).toEqual(root0Child1);
18361859
// simulate the new item is below the fold
18371860
getChildPositionInScroller.mockReturnValueOnce(1);
1861+
// add an element with the same ID as the one we are navigating to,
1862+
// otherwise we won't scroll to it
1863+
attachDivWithID(root0Child1.uid);
18381864
// trigger a navigation
18391865
targetChild.vm.$emit('navigate', root0Child1.uid);
18401866
await wrapper.vm.$nextTick();
@@ -2039,9 +2065,10 @@ describe('NavigatorCard', () => {
20392065

20402066
describe('scroll to item', () => {
20412067
it('resets the scroll position, if initiating a filter', async () => {
2042-
const wrapper = createWrapper();
2068+
attachDivWithID(root0Child0.uid);
20432069
// simulate item is above the scrollarea
20442070
getChildPositionInScroller.mockReturnValueOnce(1);
2071+
const wrapper = createWrapper();
20452072
await flushPromises();
20462073
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
20472074
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(1);
@@ -2055,43 +2082,45 @@ describe('NavigatorCard', () => {
20552082
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(0);
20562083
});
20572084

2058-
it('keeps the scroll position, if the item is already in the viewport, on navigation', async () => {
2085+
it('scrolls to item, if HIDE_DEPRECATED_TAG is picked', async () => {
2086+
attachDivWithID(root0Child0.uid);
2087+
// simulate item is in viewport
2088+
getChildPositionInScroller.mockReturnValueOnce(0);
20592089
const wrapper = createWrapper();
20602090
await flushPromises();
20612091
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
2062-
wrapper.findAll(NavigatorCardItem).at(2).vm.$emit('navigate', root0Child1.uid);
2063-
await flushPromises();
2064-
// make sure scrollToItem is not called, because active item is already in the viewport
2065-
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
2066-
// simulate header scroll
2092+
// simulate item is below the viewport
20672093
getChildPositionInScroller.mockReturnValueOnce(1);
2068-
wrapper.findAll(NavigatorCardItem).at(2).vm.$emit('navigate', root0Child0.uid);
2094+
// add the "Hide Deprecated" tag
2095+
wrapper.find(FilterInput).vm.$emit('update:selectedTags', [HIDE_DEPRECATED_TAG]);
20692096
await flushPromises();
2070-
// make sure scrollToItem is not called, because active item is already in the viewport
2097+
// assert current active item is still scrolled to
20712098
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
2099+
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(1);
20722100
});
20732101

2074-
it('scrolls to item, if outside of visible viewport, on page navigation', async () => {
2102+
it('keeps the scroll position, if the item is already in the viewport, on navigation', async () => {
2103+
attachDivWithID(root0Child0.uid);
20752104
const wrapper = createWrapper();
20762105
await flushPromises();
2106+
// assert scrollToItem is not called, because its "in view"
20772107
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
2078-
getChildPositionInScroller.mockReturnValueOnce(-1);
2079-
// scroll to the item
2108+
attachDivWithID(root0Child1.uid);
20802109
wrapper.findAll(NavigatorCardItem).at(2).vm.$emit('navigate', root0Child1.uid);
20812110
await flushPromises();
2082-
// make sure scrollToItem is called
2083-
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
2084-
// assert it was called for the 3-rd item
2085-
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(2);
2086-
// assert scrolling beyond
2111+
// make sure scrollToItem is not called again, because active item is still in the viewport
2112+
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
2113+
// simulate item is below the fold and assert navigating to new place scrolls the item
20872114
getChildPositionInScroller.mockReturnValueOnce(1);
2088-
// scroll to the item
2089-
wrapper.findAll(NavigatorCardItem).at(2).vm.$emit('navigate', root0Child0.uid);
2115+
// prepare
2116+
attachDivWithID(root0Child1GrandChild0.uid);
2117+
// navigate
2118+
wrapper.findAll(NavigatorCardItem).at(2).vm.$emit('navigate', root0Child1GrandChild0.uid);
20902119
await flushPromises();
2091-
// make sure scrollToItem is called
2092-
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(2);
2093-
// assert it was called for the 3-rd item
2094-
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(1);
2120+
// assert scrollToItem is called, because item was under the fold
2121+
expect(RecycleScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
2122+
expect(RecycleScrollerStub.methods.scrollToItem)
2123+
.toHaveBeenLastCalledWith(3);
20952124
});
20962125

20972126
it('scrolls to the focused item, if not visible', async () => {

0 commit comments

Comments
 (0)