Commit 288af487 authored by Phil Hughes's avatar Phil Hughes

Fixes a rendering bug in the widget extensions virtual scroller

Sometimes when scrolling a few times up an down the content
will not get rendered the 2nd time a row is viewed.
This changes the virtual scroller to use our more recent virtual scroller
library which should be more performant.
parent 24721913
...@@ -8,9 +8,9 @@ import { ...@@ -8,9 +8,9 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import { once } from 'lodash'; import { once } from 'lodash';
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import { DynamicScroller, DynamicScrollerItem } from 'vendor/vue-virtual-scroller';
import api from '~/api'; import api from '~/api';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
import Poll from '~/lib/utils/poll'; import Poll from '~/lib/utils/poll';
import { EXTENSION_ICON_CLASS, EXTENSION_ICONS } from '../../constants'; import { EXTENSION_ICON_CLASS, EXTENSION_ICONS } from '../../constants';
import StatusIcon from './status_icon.vue'; import StatusIcon from './status_icon.vue';
...@@ -30,10 +30,11 @@ export default { ...@@ -30,10 +30,11 @@ export default {
GlButton, GlButton,
GlLoadingIcon, GlLoadingIcon,
GlIntersectionObserver, GlIntersectionObserver,
SmartVirtualList,
StatusIcon, StatusIcon,
Actions, Actions,
ChildContent, ChildContent,
DynamicScroller,
DynamicScrollerItem,
}, },
directives: { directives: {
SafeHtml: GlSafeHtmlDirective, SafeHtml: GlSafeHtmlDirective,
...@@ -186,7 +187,7 @@ export default { ...@@ -186,7 +187,7 @@ export default {
this.fetchFullData(this.$props) this.fetchFullData(this.$props)
.then((data) => { .then((data) => {
this.loadingState = null; this.loadingState = null;
this.fullData = data; this.fullData = data.map((x, i) => ({ id: i, ...x }));
}) })
.catch((e) => { .catch((e) => {
this.loadingState = LOADING_STATES.expandedError; this.loadingState = LOADING_STATES.expandedError;
...@@ -276,34 +277,33 @@ export default { ...@@ -276,34 +277,33 @@ export default {
<div v-if="isLoadingExpanded" class="report-block-container"> <div v-if="isLoadingExpanded" class="report-block-container">
<gl-loading-icon size="sm" inline /> {{ __('Loading...') }} <gl-loading-icon size="sm" inline /> {{ __('Loading...') }}
</div> </div>
<smart-virtual-list <dynamic-scroller
v-else-if="hasFullData" v-else-if="hasFullData"
:length="fullData.length" :items="fullData"
:remain="20" :min-item-size="32"
:size="32"
wtag="ul"
wclass="report-block-list"
class="report-block-container gl-px-5 gl-py-0" class="report-block-container gl-px-5 gl-py-0"
> >
<li <template #default="{ item, index, active }">
v-for="(data, index) in fullData" <dynamic-scroller-item :item="item" :active="active" :class="{ active }">
:key="data.id" <div
:class="{ :class="{
'gl-border-b-solid gl-border-b-1 gl-border-gray-100': index !== fullData.length - 1, 'gl-border-b-solid gl-border-b-1 gl-border-gray-100': index !== fullData.length - 1,
}" }"
class="gl-py-3 gl-pl-7" class="gl-py-3 gl-pl-7"
data-testid="extension-list-item" data-testid="extension-list-item"
> >
<gl-intersection-observer <gl-intersection-observer
:options="{ rootMargin: '100px', thresholds: 0.1 }" :options="{ rootMargin: '100px', thresholds: 0.1 }"
class="gl-w-full" class="gl-w-full"
@appear="appear(index)" @appear="appear(index)"
@disappear="disappear(index)" @disappear="disappear(index)"
> >
<child-content :data="data" :widget-label="widgetLabel" :level="2" /> <child-content :data="item" :widget-label="widgetLabel" :level="2" />
</gl-intersection-observer> </gl-intersection-observer>
</li> </div>
</smart-virtual-list> </dynamic-scroller-item>
</template>
</dynamic-scroller>
<div <div
:class="{ show: showFade }" :class="{ show: showFade }"
class="fade mr-extenson-scrim gl-absolute gl-left-0 gl-bottom-0 gl-w-full gl-h-7 gl-pointer-events-none" class="fade mr-extenson-scrim gl-absolute gl-left-0 gl-bottom-0 gl-w-full gl-h-7 gl-pointer-events-none"
......
...@@ -745,6 +745,10 @@ $tabs-holder-z-index: 250; ...@@ -745,6 +745,10 @@ $tabs-holder-z-index: 250;
} }
} }
.mr-section-container .resize-observer > object {
height: 0;
}
// TODO: Move to GitLab UI // TODO: Move to GitLab UI
.mr-extenson-scrim { .mr-extenson-scrim {
background: linear-gradient(to bottom, rgba($gray-light, 0), rgba($gray-light, 1)); background: linear-gradient(to bottom, rgba($gray-light, 0), rgba($gray-light, 1));
......
...@@ -100,15 +100,15 @@ describe('Accessibility extension', () => { ...@@ -100,15 +100,15 @@ describe('Accessibility extension', () => {
await waitForPromises(); await waitForPromises();
}); });
it('displays all report list items', async () => { it('displays all report list items in viewport', async () => {
expect(findAllExtensionListItems()).toHaveLength(10); expect(findAllExtensionListItems()).toHaveLength(7);
}); });
it('displays report list item formatted', () => { it('displays report list item formatted', () => {
const text = { const text = {
newError: trimText(findAllExtensionListItems().at(0).text()), newError: trimText(findAllExtensionListItems().at(0).text()),
resolvedError: findAllExtensionListItems().at(3).text(), resolvedError: findAllExtensionListItems().at(3).text(),
existingError: trimText(findAllExtensionListItems().at(8).text()), existingError: trimText(findAllExtensionListItems().at(6).text()),
}; };
expect(text.newError).toBe( expect(text.newError).toBe(
...@@ -118,7 +118,7 @@ describe('Accessibility extension', () => { ...@@ -118,7 +118,7 @@ describe('Accessibility extension', () => {
'The accessibility scanning found an error of the following type: WCAG2AA.Principle1.Guideline1_1.1_1_1.H30.2 Learn more Message: Img element is the only content of the link, but is missing alt text. The alt text should describe the purpose of the link.', 'The accessibility scanning found an error of the following type: WCAG2AA.Principle1.Guideline1_1.1_1_1.H30.2 Learn more Message: Img element is the only content of the link, but is missing alt text. The alt text should describe the purpose of the link.',
); );
expect(text.existingError).toBe( expect(text.existingError).toBe(
'The accessibility scanning found an error of the following type: WCAG2AA.Principle2.Guideline2_4.2_4_1.H64.1 Learn more Message: Iframe element requires a non-empty title attribute that identifies the frame.', 'The accessibility scanning found an error of the following type: WCAG2AA.Principle1.Guideline1_1.1_1_1.H37 Learn more Message: Img element missing an alt attribute. Use the alt attribute to specify a short text alternative.',
); );
}); });
}); });
......
...@@ -947,6 +947,8 @@ describe('MrWidgetOptions', () => { ...@@ -947,6 +947,8 @@ describe('MrWidgetOptions', () => {
wrapper.find('[data-testid="widget-extension-top-level"]').find(GlDropdown).exists(), wrapper.find('[data-testid="widget-extension-top-level"]').find(GlDropdown).exists(),
).toBe(false); ).toBe(false);
await nextTick();
const collapsedSection = wrapper.find('[data-testid="widget-extension-collapsed-section"]'); const collapsedSection = wrapper.find('[data-testid="widget-extension-collapsed-section"]');
expect(collapsedSection.exists()).toBe(true); expect(collapsedSection.exists()).toBe(true);
expect(collapsedSection.text()).toContain('Hello world'); expect(collapsedSection.text()).toContain('Hello world');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment