Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
184906087f
commit
f21dc6a978
7 changed files with 158 additions and 14 deletions
|
@ -36,14 +36,45 @@ export default {
|
|||
tooltips: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.observer = new MutationObserver(mutations => {
|
||||
mutations.forEach(mutation => {
|
||||
this.dispose(mutation.removedNodes);
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.observer.disconnect();
|
||||
},
|
||||
methods: {
|
||||
addTooltips(elements, config) {
|
||||
const newTooltips = elements
|
||||
.filter(element => !this.tooltipExists(element))
|
||||
.map(element => newTooltip(element, config));
|
||||
|
||||
newTooltips.forEach(tooltip => this.observe(tooltip));
|
||||
|
||||
this.tooltips.push(...newTooltips);
|
||||
},
|
||||
observe(tooltip) {
|
||||
this.observer.observe(tooltip.target.parentElement, {
|
||||
childList: true,
|
||||
});
|
||||
},
|
||||
dispose(elements) {
|
||||
if (!elements) {
|
||||
this.tooltips = [];
|
||||
return;
|
||||
}
|
||||
|
||||
elements.forEach(element => {
|
||||
const index = this.tooltips.findIndex(tooltip => tooltip.target === element);
|
||||
|
||||
if (index > -1) {
|
||||
this.tooltips.splice(index, 1);
|
||||
}
|
||||
});
|
||||
},
|
||||
tooltipExists(element) {
|
||||
return this.tooltips.some(tooltip => tooltip.target === element);
|
||||
},
|
||||
|
|
|
@ -10,9 +10,15 @@ const EVENTS_MAP = {
|
|||
};
|
||||
|
||||
const DEFAULT_TRIGGER = 'hover focus';
|
||||
const APP_ELEMENT_ID = 'gl-tooltips-app';
|
||||
|
||||
const tooltipsApp = () => {
|
||||
if (!app) {
|
||||
const container = document.createElement('div');
|
||||
|
||||
container.setAttribute('id', APP_ELEMENT_ID);
|
||||
document.body.appendChild(container);
|
||||
|
||||
app = new Vue({
|
||||
render(h) {
|
||||
return h(Tooltips, {
|
||||
|
@ -22,7 +28,7 @@ const tooltipsApp = () => {
|
|||
ref: 'tooltips',
|
||||
});
|
||||
},
|
||||
}).$mount();
|
||||
}).$mount(container);
|
||||
}
|
||||
|
||||
return app;
|
||||
|
@ -56,3 +62,12 @@ export const initTooltips = (selector, config = {}) => {
|
|||
|
||||
return tooltipsApp();
|
||||
};
|
||||
|
||||
export const dispose = elements => {
|
||||
return tooltipsApp().$refs.tooltips.dispose(elements);
|
||||
};
|
||||
|
||||
export const destroy = () => {
|
||||
tooltipsApp().$destroy();
|
||||
app = null;
|
||||
};
|
||||
|
|
|
@ -216,7 +216,10 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def show_onboarding_issues_experiment?
|
||||
!helpers.in_subscription_flow? && !helpers.in_invitation_flow? && !helpers.in_oauth_flow?
|
||||
!helpers.in_subscription_flow? &&
|
||||
!helpers.in_invitation_flow? &&
|
||||
!helpers.in_oauth_flow? &&
|
||||
!helpers.in_trial_flow?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -59,7 +59,9 @@ is **not** `19.03.0`. See [troubleshooting information](#error-response-from-dae
|
|||
|
||||
## Supported languages and frameworks
|
||||
|
||||
The following table shows which languages, package managers and frameworks are supported and which tools are used.
|
||||
GitLab SAST supports a variety of languages, package managers, and frameworks. Our SAST security scanners also feature automatic language detection which works even for mixed-language projects. If any supported language is detected in project source code we will automatically run the appropriate SAST analyzers.
|
||||
|
||||
You can also [view our language roadmap](https://about.gitlab.com/direction/secure/static-analysis/sast/#language-support) and [request other language support by opening an issue](https://gitlab.com/groups/gitlab-org/-/epics/297).
|
||||
|
||||
| Language (package managers) / framework | Scan tool | Introduced in GitLab Version |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
|
|
@ -84,7 +84,9 @@ const useMockObserver = (key, createMock) => {
|
|||
mockObserver.$_triggerObserve(...args);
|
||||
};
|
||||
|
||||
return { trigger };
|
||||
const observersCount = () => mockObserver.$_observers.length;
|
||||
|
||||
return { trigger, observersCount };
|
||||
};
|
||||
|
||||
export const useMockIntersectionObserver = () =>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlTooltip } from '@gitlab/ui';
|
||||
import { useMockMutationObserver } from 'helpers/mock_dom_observer';
|
||||
import Tooltips from '~/tooltips/components/tooltips.vue';
|
||||
|
||||
describe('tooltips/components/tooltips.vue', () => {
|
||||
const { trigger: triggerMutate, observersCount } = useMockMutationObserver();
|
||||
let wrapper;
|
||||
|
||||
const buildWrapper = () => {
|
||||
|
@ -20,11 +22,16 @@ describe('tooltips/components/tooltips.vue', () => {
|
|||
target.setAttribute(name, defaults[name]);
|
||||
});
|
||||
|
||||
document.body.appendChild(target);
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
const allTooltips = () => wrapper.findAll(GlTooltip);
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
describe('addTooltips', () => {
|
||||
|
@ -91,4 +98,63 @@ describe('tooltips/components/tooltips.vue', () => {
|
|||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('dispose', () => {
|
||||
beforeEach(() => {
|
||||
buildWrapper();
|
||||
});
|
||||
|
||||
it('removes all tooltips when elements is nil', async () => {
|
||||
wrapper.vm.addTooltips([createTooltipTarget(), createTooltipTarget()]);
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
wrapper.vm.dispose();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(allTooltips()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('removes the tooltips that target the elements specified', async () => {
|
||||
const target = createTooltipTarget();
|
||||
|
||||
wrapper.vm.addTooltips([target, createTooltipTarget()]);
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
wrapper.vm.dispose([target]);
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(allTooltips()).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('observe', () => {
|
||||
beforeEach(() => {
|
||||
buildWrapper();
|
||||
});
|
||||
|
||||
it('removes tooltip when target is removed from the document', async () => {
|
||||
const target = createTooltipTarget();
|
||||
|
||||
wrapper.vm.addTooltips([target, createTooltipTarget()]);
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
triggerMutate(document.body, {
|
||||
entry: { removedNodes: [target] },
|
||||
options: { childList: true },
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(allTooltips()).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('disconnects mutation observer on beforeDestroy', () => {
|
||||
buildWrapper();
|
||||
wrapper.vm.addTooltips([createTooltipTarget()]);
|
||||
|
||||
expect(observersCount()).toBe(1);
|
||||
|
||||
wrapper.destroy();
|
||||
expect(observersCount()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { initTooltips } from '~/tooltips';
|
||||
import { initTooltips, dispose, destroy } from '~/tooltips';
|
||||
|
||||
describe('tooltips/index.js', () => {
|
||||
let tooltipsApp;
|
||||
|
||||
const createTooltipTarget = () => {
|
||||
const target = document.createElement('button');
|
||||
const attributes = {
|
||||
|
@ -13,22 +15,31 @@ describe('tooltips/index.js', () => {
|
|||
|
||||
target.classList.add('has-tooltip');
|
||||
|
||||
document.body.appendChild(target);
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
const buildTooltipsApp = () => {
|
||||
tooltipsApp = initTooltips('.has-tooltip');
|
||||
};
|
||||
|
||||
const triggerEvent = (target, eventName = 'mouseenter') => {
|
||||
const event = new Event(eventName);
|
||||
|
||||
target.dispatchEvent(event);
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
document.body.childNodes.forEach(node => node.remove());
|
||||
destroy();
|
||||
});
|
||||
|
||||
describe('initTooltip', () => {
|
||||
it('attaches a GlTooltip for the elements specified in the selector', async () => {
|
||||
const target = createTooltipTarget();
|
||||
const tooltipsApp = initTooltips('.has-tooltip');
|
||||
|
||||
document.body.appendChild(tooltipsApp.$el);
|
||||
document.body.appendChild(target);
|
||||
buildTooltipsApp();
|
||||
|
||||
triggerEvent(target);
|
||||
|
||||
|
@ -40,13 +51,8 @@ describe('tooltips/index.js', () => {
|
|||
|
||||
it('supports triggering a tooltip in custom events', async () => {
|
||||
const target = createTooltipTarget();
|
||||
const tooltipsApp = initTooltips('.has-tooltip', {
|
||||
triggers: 'click',
|
||||
});
|
||||
|
||||
document.body.appendChild(tooltipsApp.$el);
|
||||
document.body.appendChild(target);
|
||||
|
||||
buildTooltipsApp();
|
||||
triggerEvent(target, 'click');
|
||||
|
||||
await tooltipsApp.$nextTick();
|
||||
|
@ -55,4 +61,23 @@ describe('tooltips/index.js', () => {
|
|||
expect(document.querySelector('.gl-tooltip').innerHTML).toContain('default title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dispose', () => {
|
||||
it('removes tooltips that target the elements specified', async () => {
|
||||
const target = createTooltipTarget();
|
||||
|
||||
buildTooltipsApp();
|
||||
triggerEvent(target);
|
||||
|
||||
await tooltipsApp.$nextTick();
|
||||
|
||||
expect(document.querySelector('.gl-tooltip')).not.toBe(null);
|
||||
|
||||
dispose([target]);
|
||||
|
||||
await tooltipsApp.$nextTick();
|
||||
|
||||
expect(document.querySelector('.gl-tooltip')).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue