Commit 931d4af9 authored by Denys Mishunov's avatar Denys Mishunov

Added shared Clone Dropdown button component

Shared component used for cloning functionality on Projects and
Snippets.

Also a new shared 'getBaseProtocol' url utility has been added
parent 1ed71f63
......@@ -318,3 +318,11 @@ export const setUrlParams = (params, url = window.location.href, clearParams = f
export function urlIsDifferent(url, compare = String(window.location)) {
return url !== compare;
}
export function getHTTPProtocol(url) {
if (!url) {
return window.location.protocol.slice(0, -1);
}
const protocol = url.split(':');
return protocol.length > 1 ? protocol[0] : undefined;
}
<script>
import {
GlNewDropdown,
GlNewDropdownHeader,
GlFormInputGroup,
GlNewButton,
GlIcon,
GlTooltipDirective,
} from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { getHTTPProtocol } from '~/lib/utils/url_utility';
export default {
components: {
GlNewDropdown,
GlNewDropdownHeader,
GlFormInputGroup,
GlNewButton,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
sshLink: {
type: String,
required: false,
default: '',
},
httpLink: {
type: String,
required: false,
default: '',
},
},
computed: {
httpLabel() {
const protocol = this.httpLink ? getHTTPProtocol(this.httpLink)?.toUpperCase() : '';
return sprintf(__('Clone with %{protocol}'), { protocol });
},
},
labels: {
defaultLabel: __('Clone'),
ssh: __('Clone with SSH'),
},
copyURLTooltip: __('Copy URL'),
};
</script>
<template>
<gl-new-dropdown :text="$options.labels.defaultLabel" category="primary" variant="info">
<div class="pb-2 mx-1">
<template v-if="sshLink">
<gl-new-dropdown-header>{{ $options.labels.ssh }}</gl-new-dropdown-header>
<div class="mx-3">
<gl-form-input-group :value="sshLink" readonly select-on-click>
<template #append>
<gl-new-button
v-gl-tooltip.hover
:title="$options.copyURLTooltip"
:data-clipboard-text="sshLink"
>
<gl-icon name="copy-to-clipboard" :title="$options.copyURLTooltip" />
</gl-new-button>
</template>
</gl-form-input-group>
</div>
</template>
<template v-if="httpLink">
<gl-new-dropdown-header>{{ httpLabel }}</gl-new-dropdown-header>
<div class="mx-3">
<gl-form-input-group :value="httpLink" readonly select-on-click>
<template #append>
<gl-new-button
v-gl-tooltip.hover
:title="$options.copyURLTooltip"
:data-clipboard-text="httpLink"
>
<gl-icon name="copy-to-clipboard" :title="$options.copyURLTooltip" />
</gl-new-button>
</template>
</gl-form-input-group>
</div>
</template>
</div>
</gl-new-dropdown>
</template>
......@@ -4025,6 +4025,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
msgid "Clone with %{protocol}"
msgstr ""
msgid "Clone with KRB5"
msgstr ""
......
......@@ -485,4 +485,30 @@ describe('URL utility', () => {
);
});
});
describe('getHTTPProtocol', () => {
const httpProtocol = 'http:';
const httpsProtocol = 'https:';
it.each([[httpProtocol], [httpsProtocol]])(
'when no url passed, returns correct protocol for %i from window location',
protocol => {
setWindowLocation({
protocol,
});
expect(urlUtils.getHTTPProtocol()).toBe(protocol.slice(0, -1));
},
);
it.each`
url | expectation
${'not-a-url'} | ${undefined}
${'wss://example.com'} | ${'wss'}
${'https://foo.bar'} | ${'https'}
${'http://foo.bar'} | ${'http'}
${'http://foo.bar:8080'} | ${'http'}
`('returns correct protocol for $url', ({ url, expectation }) => {
expect(urlUtils.getHTTPProtocol(url)).toBe(expectation);
});
});
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
<gl-new-dropdown-stub
category="primary"
headertext=""
size="medium"
text="Clone"
variant="info"
>
<div
class="pb-2 mx-1"
>
<gl-new-dropdown-header-stub>
Clone with SSH
</gl-new-dropdown-header-stub>
<div
class="mx-3"
>
<div
readonly="readonly"
>
<b-input-group-stub
tag="div"
>
<b-input-group-prepend-stub
tag="div"
>
<!---->
</b-input-group-prepend-stub>
<b-form-input-stub
class="gl-form-input"
debounce="0"
readonly="true"
type="text"
value="ssh://foo.bar"
/>
<b-input-group-append-stub
tag="div"
>
<gl-new-button-stub
category="tertiary"
data-clipboard-text="ssh://foo.bar"
icon=""
size="medium"
title="Copy URL"
variant="default"
>
<gl-icon-stub
name="copy-to-clipboard"
size="16"
title="Copy URL"
/>
</gl-new-button-stub>
</b-input-group-append-stub>
</b-input-group-stub>
</div>
</div>
<gl-new-dropdown-header-stub>
Clone with HTTP
</gl-new-dropdown-header-stub>
<div
class="mx-3"
>
<div
readonly="readonly"
>
<b-input-group-stub
tag="div"
>
<b-input-group-prepend-stub
tag="div"
>
<!---->
</b-input-group-prepend-stub>
<b-form-input-stub
class="gl-form-input"
debounce="0"
readonly="true"
type="text"
value="http://foo.bar"
/>
<b-input-group-append-stub
tag="div"
>
<gl-new-button-stub
category="tertiary"
data-clipboard-text="http://foo.bar"
icon=""
size="medium"
title="Copy URL"
variant="default"
>
<gl-icon-stub
name="copy-to-clipboard"
size="16"
title="Copy URL"
/>
</gl-new-button-stub>
</b-input-group-append-stub>
</b-input-group-stub>
</div>
</div>
</div>
</gl-new-dropdown-stub>
`;
import CloneDropdown from '~/vue_shared/components/clone_dropdown.vue';
import { shallowMount } from '@vue/test-utils';
import { GlFormInputGroup, GlNewDropdownHeader } from '@gitlab/ui';
describe('Clone Dropdown Button', () => {
let wrapper;
const sshLink = 'ssh://foo.bar';
const httpLink = 'http://foo.bar';
const httpsLink = 'https://foo.bar';
const defaultPropsData = {
sshLink,
httpLink,
};
const createComponent = (propsData = defaultPropsData) => {
wrapper = shallowMount(CloneDropdown, {
propsData,
stubs: {
'gl-form-input-group': GlFormInputGroup,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('rendering', () => {
it('matches the snapshot', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
it.each`
name | index | value
${'SSH'} | ${0} | ${sshLink}
${'HTTP'} | ${1} | ${httpLink}
`('renders correct link and a copy-button for $name', ({ index, value }) => {
createComponent();
const group = wrapper.findAll(GlFormInputGroup).at(index);
expect(group.props('value')).toBe(value);
expect(group.contains(GlFormInputGroup)).toBe(true);
});
it.each`
name | value
${'sshLink'} | ${sshLink}
${'httpLink'} | ${httpLink}
`('does not fail if only $name is set', ({ name, value }) => {
createComponent({ [name]: value });
expect(wrapper.find(GlFormInputGroup).props('value')).toBe(value);
expect(wrapper.findAll(GlNewDropdownHeader).length).toBe(1);
});
});
describe('functionality', () => {
it.each`
name | value
${'sshLink'} | ${null}
${'httpLink'} | ${null}
`('allows null values for the props', ({ name, value }) => {
createComponent({ ...defaultPropsData, [name]: value });
expect(wrapper.findAll(GlNewDropdownHeader).length).toBe(1);
});
it('correctly calculates httpLabel for HTTPS protocol', () => {
createComponent({ httpLink: httpsLink });
expect(wrapper.find(GlNewDropdownHeader).text()).toContain('HTTPS');
});
});
});
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