Commit 08a19d0a authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch...

Merge branch '207467-add-gradle-setup-and-install-commands-to-the-package-details-page' into 'master'

Add persisted selection component and fix code instruction

See merge request gitlab-org/gitlab!55604
parents 9197f9c5 20a35465
......@@ -12,9 +12,17 @@ export default {
GlLink,
GlSprintf,
},
data() {
return {
instructionType: 'maven',
};
},
computed: {
...mapState(['mavenHelpPath']),
...mapGetters(['mavenInstallationXml', 'mavenInstallationCommand', 'mavenSetupXml']),
showMaven() {
return this.instructionType === 'maven';
},
},
i18n: {
xmlText: s__(
......@@ -36,50 +44,51 @@ export default {
<div>
<h3 class="gl-font-lg">{{ __('Installation') }}</h3>
<p>
<gl-sprintf :message="$options.i18n.xmlText">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
<template v-if="showMaven">
<p>
<gl-sprintf :message="$options.i18n.xmlText">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
<code-instruction
:label="s__('PackageRegistry|Maven XML')"
:instruction="mavenInstallationXml"
:copy-text="s__('PackageRegistry|Copy Maven XML')"
multiline
:tracking-action="$options.trackingActions.COPY_MAVEN_XML"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<code-instruction
:instruction="mavenInstallationXml"
:copy-text="s__('PackageRegistry|Copy Maven XML')"
:tracking-action="$options.trackingActions.COPY_MAVEN_XML"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
multiline
/>
<code-instruction
:label="s__('PackageRegistry|Maven Command')"
:instruction="mavenInstallationCommand"
:copy-text="s__('PackageRegistry|Copy Maven command')"
:tracking-action="$options.trackingActions.COPY_MAVEN_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<code-instruction
:label="s__('PackageRegistry|Maven Command')"
:instruction="mavenInstallationCommand"
:copy-text="s__('PackageRegistry|Copy Maven command')"
:tracking-action="$options.trackingActions.COPY_MAVEN_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
<p>
<gl-sprintf :message="$options.i18n.setupText">
<template #code="{ content }">
<code>{{ content }}</code>
<h3 class="gl-font-lg">{{ s__('PackageRegistry|Registry setup') }}</h3>
<p>
<gl-sprintf :message="$options.i18n.setupText">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
<code-instruction
:instruction="mavenSetupXml"
:copy-text="s__('PackageRegistry|Copy Maven registry XML')"
:tracking-action="$options.trackingActions.COPY_MAVEN_SETUP"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
multiline
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
<gl-link :href="mavenHelpPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
<code-instruction
:instruction="mavenSetupXml"
:copy-text="s__('PackageRegistry|Copy Maven registry XML')"
multiline
:tracking-action="$options.trackingActions.COPY_MAVEN_SETUP"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
<gl-link :href="mavenHelpPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</template>
</div>
</template>
......@@ -56,27 +56,29 @@ export default {
</script>
<template>
<div v-if="!multiline" class="gl-mb-3">
<div>
<label v-if="label" :for="generateFormId('instruction-input')">{{ label }}</label>
<div class="input-group gl-mb-3">
<input
:id="generateFormId('instruction-input')"
:value="instruction"
type="text"
class="form-control gl-font-monospace"
data-testid="instruction-input"
readonly
@copy="trackCopy"
/>
<span class="input-group-append" data-testid="instruction-button" @click="trackCopy">
<clipboard-button :text="instruction" :title="copyText" class="input-group-text" />
</span>
<div v-if="!multiline" class="gl-mb-3">
<div class="input-group gl-mb-3">
<input
:id="generateFormId('instruction-input')"
:value="instruction"
type="text"
class="form-control gl-font-monospace"
data-testid="instruction-input"
readonly
@copy="trackCopy"
/>
<span class="input-group-append" data-testid="instruction-button" @click="trackCopy">
<clipboard-button :text="instruction" :title="copyText" class="input-group-text" />
</span>
</div>
</div>
</div>
<div v-else>
<pre class="gl-font-monospace" data-testid="multiline-instruction" @copy="trackCopy">{{
instruction
}}</pre>
<div v-else>
<pre class="gl-font-monospace" data-testid="multiline-instruction" @copy="trackCopy">{{
instruction
}}</pre>
</div>
</div>
</template>
<script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
export default {
name: 'PersistedDropdownSelection',
components: {
GlDropdown,
GlDropdownItem,
LocalStorageSync,
},
props: {
options: {
type: Array,
required: true,
},
storageKey: {
type: String,
required: true,
},
},
data() {
return {
selected: null,
};
},
computed: {
dropdownText() {
const selected = this.parsedOptions.find((o) => o.selected);
return selected?.label || this.options[0].label;
},
parsedOptions() {
return this.options.map((o) => ({ ...o, selected: o.value === this.selected }));
},
},
methods: {
setSelected(value) {
this.selected = value;
this.$emit('change', value);
},
},
};
</script>
<template>
<local-storage-sync :storage-key="storageKey" :value="selected" @input="setSelected">
<gl-dropdown :text="dropdownText" lazy>
<gl-dropdown-item
v-for="option in parsedOptions"
:key="option.value"
:is-checked="option.selected"
:is-check-item="true"
@click="setSelected(option.value)"
>
{{ option.label }}
</gl-dropdown-item>
</gl-dropdown>
</local-storage-sync>
</template>
......@@ -21617,9 +21617,6 @@ msgstr ""
msgid "PackageRegistry|Maven Command"
msgstr ""
msgid "PackageRegistry|Maven XML"
msgstr ""
msgid "PackageRegistry|NuGet"
msgstr ""
......@@ -21656,6 +21653,9 @@ msgstr ""
msgid "PackageRegistry|Recipe: %{recipe}"
msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
msgid "PackageRegistry|Remove package"
msgstr ""
......
......@@ -17,7 +17,7 @@ exports[`MavenInstallation renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy Maven XML"
instruction="foo/xml"
label="Maven XML"
label=""
multiline="true"
trackingaction="copy_maven_xml"
trackinglabel="code_instruction"
......
......@@ -2,20 +2,26 @@
exports[`Package code instruction multiline to match the snapshot 1`] = `
<div>
<pre
class="gl-font-monospace"
data-testid="multiline-instruction"
<label
for="instruction-input_3"
>
this is some
foo_label
</label>
<div>
<pre
class="gl-font-monospace"
data-testid="multiline-instruction"
>
this is some
multiline text
</pre>
</pre>
</div>
</div>
`;
exports[`Package code instruction single line to match the default snapshot 1`] = `
<div
class="gl-mb-3"
>
<div>
<label
for="instruction-input_2"
>
......@@ -23,42 +29,46 @@ exports[`Package code instruction single line to match the default snapshot 1`]
</label>
<div
class="input-group gl-mb-3"
class="gl-mb-3"
>
<input
class="form-control gl-font-monospace"
data-testid="instruction-input"
id="instruction-input_2"
readonly="readonly"
type="text"
/>
<span
class="input-group-append"
data-testid="instruction-button"
<div
class="input-group gl-mb-3"
>
<button
aria-label="Copy this value"
class="btn input-group-text btn-default btn-md gl-button btn-default-secondary btn-icon"
data-clipboard-text="npm i @my-package"
title="Copy npm install command"
type="button"
<input
class="form-control gl-font-monospace"
data-testid="instruction-input"
id="instruction-input_2"
readonly="readonly"
type="text"
/>
<span
class="input-group-append"
data-testid="instruction-button"
>
<!---->
<svg
aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="copy-to-clipboard-icon"
<button
aria-label="Copy this value"
class="btn input-group-text btn-default btn-md gl-button btn-default-secondary btn-icon"
data-clipboard-text="npm i @my-package"
title="Copy npm install command"
type="button"
>
<use
href="#copy-to-clipboard"
/>
</svg>
<!---->
</button>
</span>
<!---->
<svg
aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="copy-to-clipboard-icon"
>
<use
href="#copy-to-clipboard"
/>
</svg>
<!---->
</button>
</span>
</div>
</div>
</div>
`;
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import component from '~/vue_shared/components/registry/persisted_dropdown_selection.vue';
describe('Persisted dropdown selection', () => {
let wrapper;
const defaultProps = {
storageKey: 'foo_bar',
options: [
{ value: 'maven', label: 'Maven' },
{ value: 'gradle', label: 'Gradle' },
],
};
function createComponent({ props = {}, data = {} } = {}) {
wrapper = shallowMount(component, {
propsData: {
...defaultProps,
...props,
},
data() {
return data;
},
});
}
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
afterEach(() => {
wrapper.destroy();
});
describe('local storage sync', () => {
it('uses the local storage sync component', () => {
createComponent();
expect(findLocalStorageSync().exists()).toBe(true);
});
it('passes the right props', () => {
createComponent({ data: { selected: 'foo' } });
expect(findLocalStorageSync().props()).toMatchObject({
storageKey: defaultProps.storageKey,
value: 'foo',
});
});
it('on input event updates the model and emits event', async () => {
const inputPayload = 'bar';
createComponent();
findLocalStorageSync().vm.$emit('input', inputPayload);
await nextTick();
expect(wrapper.emitted('change')).toStrictEqual([[inputPayload]]);
expect(findLocalStorageSync().props('value')).toBe(inputPayload);
});
});
describe('dropdown', () => {
it('has a dropdown component', () => {
createComponent();
expect(findDropdown().exists()).toBe(true);
});
describe('dropdown text', () => {
it('when no selection shows the first', () => {
createComponent();
expect(findDropdown().props('text')).toBe('Maven');
});
it('when an option is selected, shows that option label', () => {
createComponent({ data: { selected: defaultProps.options[1].value } });
expect(findDropdown().props('text')).toBe('Gradle');
});
});
describe('dropdown items', () => {
it('has one item for each option', () => {
createComponent();
expect(findDropdownItems()).toHaveLength(defaultProps.options.length);
});
it('binds the correct props', () => {
createComponent({ data: { selected: defaultProps.options[0].value } });
expect(findDropdownItems().at(0).props()).toMatchObject({
isChecked: true,
isCheckItem: true,
});
expect(findDropdownItems().at(1).props()).toMatchObject({
isChecked: false,
isCheckItem: true,
});
});
it('on click updates the data and emits event', async () => {
createComponent({ data: { selected: defaultProps.options[0].value } });
expect(findDropdownItems().at(0).props('isChecked')).toBe(true);
findDropdownItems().at(1).vm.$emit('click');
await nextTick();
expect(wrapper.emitted('change')).toStrictEqual([['gradle']]);
expect(findDropdownItems().at(0).props('isChecked')).toBe(false);
expect(findDropdownItems().at(1).props('isChecked')).toBe(true);
});
});
});
});
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