Add persisted dropdown component

- source
- tests
parent 40c19a06
......@@ -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,6 +44,7 @@ export default {
<div>
<h3 class="gl-font-lg">{{ __('Installation') }}</h3>
<template v-if="showMaven">
<p>
<gl-sprintf :message="$options.i18n.xmlText">
<template #code="{ content }">
......@@ -45,12 +54,11 @@ export default {
</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"
multiline
/>
<code-instruction
......@@ -61,7 +69,7 @@ export default {
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
<h3 class="gl-font-lg">{{ s__('PackageRegistry|Registry setup') }}</h3>
<p>
<gl-sprintf :message="$options.i18n.setupText">
<template #code="{ content }">
......@@ -72,14 +80,15 @@ export default {
<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"
multiline
/>
<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,8 +56,9 @@ export default {
</script>
<template>
<div v-if="!multiline" class="gl-mb-3">
<div>
<label v-if="label" :for="generateFormId('instruction-input')">{{ label }}</label>
<div v-if="!multiline" class="gl-mb-3">
<div class="input-group gl-mb-3">
<input
:id="generateFormId('instruction-input')"
......@@ -79,4 +80,5 @@ export default {
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>
......@@ -21611,9 +21611,6 @@ msgstr ""
msgid "PackageRegistry|Maven Command"
msgstr ""
msgid "PackageRegistry|Maven XML"
msgstr ""
msgid "PackageRegistry|NuGet"
msgstr ""
......@@ -21650,6 +21647,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,6 +2,13 @@
exports[`Package code instruction multiline to match the snapshot 1`] = `
<div>
<label
for="instruction-input_3"
>
foo_label
</label>
<div>
<pre
class="gl-font-monospace"
data-testid="multiline-instruction"
......@@ -9,19 +16,21 @@ exports[`Package code instruction multiline to match the snapshot 1`] = `
this is some
multiline text
</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"
>
foo_label
</label>
<div
class="gl-mb-3"
>
<div
class="input-group gl-mb-3"
>
......@@ -60,5 +69,6 @@ exports[`Package code instruction single line to match the default snapshot 1`]
</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