Commit 7f9af793 authored by Miguel Rincon's avatar Miguel Rincon

Merge branch 'nfriend-implement-new-tag-field' into 'master'

Replace `new_tag_field.vue` placeholder with real implementation

See merge request gitlab-org/gitlab!37730
parents 098ef5a0 75bf3251
<script>
export default {
name: 'FormFieldContainer',
};
</script>
<template>
<div class="row">
<div class="col-md-6 col-lg-5 col-xl-4 gl-display-flex gl-flex-direction-column">
<slot></slot>
</div>
</div>
</template>
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import { GlFormGroup, GlFormInput, GlLink, GlSprintf } from '@gitlab/ui'; import { GlFormGroup, GlFormInput, GlLink, GlSprintf } from '@gitlab/ui';
import FormFieldContainer from './form_field_container.vue';
export default { export default {
name: 'TagFieldExisting', name: 'TagFieldExisting',
components: { GlFormGroup, GlFormInput, GlSprintf, GlLink }, components: { GlFormGroup, GlFormInput, GlSprintf, GlLink, FormFieldContainer },
computed: { computed: {
...mapState('detail', ['release', 'updateReleaseApiDocsPath']), ...mapState('detail', ['release', 'updateReleaseApiDocsPath']),
inputId() { inputId() {
...@@ -19,18 +20,16 @@ export default { ...@@ -19,18 +20,16 @@ export default {
</script> </script>
<template> <template>
<gl-form-group :label="__('Tag name')" :label-for="inputId"> <gl-form-group :label="__('Tag name')" :label-for="inputId">
<div class="row"> <form-field-container>
<div class="col-md-6 col-lg-5 col-xl-4"> <gl-form-input
<gl-form-input :id="inputId"
:id="inputId" :value="release.tagName"
:value="release.tagName" type="text"
type="text" class="form-control"
class="form-control" :aria-describedby="helpId"
:aria-describedby="helpId" disabled
disabled />
/> </form-field-container>
</div>
</div>
<template #description> <template #description>
<div :id="helpId" data-testid="tag-name-help"> <div :id="helpId" data-testid="tag-name-help">
<gl-sprintf <gl-sprintf
......
<script> <script>
import { mapState, mapActions } from 'vuex';
import { GlFormGroup, GlFormInput } from '@gitlab/ui';
import { uniqueId } from 'lodash';
import { __ } from '~/locale';
import RefSelector from '~/ref/components/ref_selector.vue';
import FormFieldContainer from './form_field_container.vue';
export default { export default {
name: 'TagFieldNew', name: 'TagFieldNew',
components: { GlFormGroup, GlFormInput, RefSelector, FormFieldContainer },
computed: {
...mapState('detail', ['projectId', 'release', 'createFrom']),
tagName: {
get() {
return this.release.tagName;
},
set(tagName) {
this.updateReleaseTagName(tagName);
},
},
createFromModel: {
get() {
return this.createFrom;
},
set(createFrom) {
this.updateCreateFrom(createFrom);
},
},
tagNameInputId() {
return uniqueId('tag-name-input-');
},
createFromSelectorId() {
return uniqueId('create-from-selector-');
},
},
methods: {
...mapActions('detail', ['updateReleaseTagName', 'updateCreateFrom']),
},
translations: {
noRefSelected: __('No source selected'),
searchPlaceholder: __('Search branches, tags, and commits'),
dropdownHeader: __('Select source'),
},
}; };
</script> </script>
<template> <template>
<div></div> <div>
<gl-form-group :label="__('Tag name')" :label-for="tagNameInputId" data-testid="tag-name-field">
<form-field-container>
<gl-form-input :id="tagNameInputId" v-model="tagName" type="text" class="form-control" />
</form-field-container>
</gl-form-group>
<gl-form-group
:label="__('Create from')"
:label-for="createFromSelectorId"
data-testid="create-from-field"
>
<form-field-container>
<ref-selector
:id="createFromSelectorId"
v-model="createFromModel"
:project-id="projectId"
:translations="$options.translations"
/>
</form-field-container>
<template #description>
{{ __('Existing branch name, tag, or commit SHA') }}
</template>
</gl-form-group>
</div>
</template> </template>
...@@ -34,6 +34,10 @@ export const fetchRelease = ({ dispatch, state }) => { ...@@ -34,6 +34,10 @@ export const fetchRelease = ({ dispatch, state }) => {
}); });
}; };
export const updateReleaseTagName = ({ commit }, tagName) =>
commit(types.UPDATE_RELEASE_TAG_NAME, tagName);
export const updateCreateFrom = ({ commit }, createFrom) =>
commit(types.UPDATE_CREATE_FROM, createFrom);
export const updateReleaseTitle = ({ commit }, title) => commit(types.UPDATE_RELEASE_TITLE, title); export const updateReleaseTitle = ({ commit }, title) => commit(types.UPDATE_RELEASE_TITLE, title);
export const updateReleaseNotes = ({ commit }, notes) => commit(types.UPDATE_RELEASE_NOTES, notes); export const updateReleaseNotes = ({ commit }, notes) => commit(types.UPDATE_RELEASE_NOTES, notes);
export const updateReleaseMilestones = ({ commit }, milestones) => export const updateReleaseMilestones = ({ commit }, milestones) =>
......
...@@ -2,6 +2,8 @@ export const REQUEST_RELEASE = 'REQUEST_RELEASE'; ...@@ -2,6 +2,8 @@ export const REQUEST_RELEASE = 'REQUEST_RELEASE';
export const RECEIVE_RELEASE_SUCCESS = 'RECEIVE_RELEASE_SUCCESS'; export const RECEIVE_RELEASE_SUCCESS = 'RECEIVE_RELEASE_SUCCESS';
export const RECEIVE_RELEASE_ERROR = 'RECEIVE_RELEASE_ERROR'; export const RECEIVE_RELEASE_ERROR = 'RECEIVE_RELEASE_ERROR';
export const UPDATE_RELEASE_TAG_NAME = 'UPDATE_RELEASE_TAG_NAME';
export const UPDATE_CREATE_FROM = 'UPDATE_CREATE_FROM';
export const UPDATE_RELEASE_TITLE = 'UPDATE_RELEASE_TITLE'; export const UPDATE_RELEASE_TITLE = 'UPDATE_RELEASE_TITLE';
export const UPDATE_RELEASE_NOTES = 'UPDATE_RELEASE_NOTES'; export const UPDATE_RELEASE_NOTES = 'UPDATE_RELEASE_NOTES';
export const UPDATE_RELEASE_MILESTONES = 'UPDATE_RELEASE_MILESTONES'; export const UPDATE_RELEASE_MILESTONES = 'UPDATE_RELEASE_MILESTONES';
......
...@@ -22,6 +22,12 @@ export default { ...@@ -22,6 +22,12 @@ export default {
state.release = undefined; state.release = undefined;
}, },
[types.UPDATE_RELEASE_TAG_NAME](state, tagName) {
state.release.tagName = tagName;
},
[types.UPDATE_CREATE_FROM](state, createFrom) {
state.createFrom = createFrom;
},
[types.UPDATE_RELEASE_TITLE](state, title) { [types.UPDATE_RELEASE_TITLE](state, title) {
state.release.name = title; state.release.name = title;
}, },
......
...@@ -27,6 +27,7 @@ export default ({ ...@@ -27,6 +27,7 @@ export default ({
releasesPagePath, releasesPagePath,
defaultBranch, defaultBranch,
createFrom: defaultBranch,
/** The Release object */ /** The Release object */
release: null, release: null,
......
...@@ -7024,6 +7024,9 @@ msgstr "" ...@@ -7024,6 +7024,9 @@ msgstr ""
msgid "Create file" msgid "Create file"
msgstr "" msgstr ""
msgid "Create from"
msgstr ""
msgid "Create group" msgid "Create group"
msgstr "" msgstr ""
...@@ -9884,6 +9887,9 @@ msgstr "" ...@@ -9884,6 +9887,9 @@ msgstr ""
msgid "Excluding merge commits. Limited to 6,000 commits." msgid "Excluding merge commits. Limited to 6,000 commits."
msgstr "" msgstr ""
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
msgid "Existing members and groups" msgid "Existing members and groups"
msgstr "" msgstr ""
...@@ -16325,6 +16331,9 @@ msgstr "" ...@@ -16325,6 +16331,9 @@ msgstr ""
msgid "No schedules" msgid "No schedules"
msgstr "" msgstr ""
msgid "No source selected"
msgstr ""
msgid "No stack trace for this error" msgid "No stack trace for this error"
msgstr "" msgstr ""
...@@ -21100,6 +21109,9 @@ msgstr "" ...@@ -21100,6 +21109,9 @@ msgstr ""
msgid "Search branches and tags" msgid "Search branches and tags"
msgstr "" msgstr ""
msgid "Search branches, tags, and commits"
msgstr ""
msgid "Search by Git revision" msgid "Search by Git revision"
msgstr "" msgstr ""
...@@ -21729,6 +21741,9 @@ msgstr "" ...@@ -21729,6 +21741,9 @@ msgstr ""
msgid "Select shards to replicate" msgid "Select shards to replicate"
msgstr "" msgstr ""
msgid "Select source"
msgstr ""
msgid "Select source branch" msgid "Select source branch"
msgstr "" msgstr ""
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlFormInput } from '@gitlab/ui';
import TagFieldNew from '~/releases/components/tag_field_new.vue'; import TagFieldNew from '~/releases/components/tag_field_new.vue';
import createStore from '~/releases/stores'; import createStore from '~/releases/stores';
import createDetailModule from '~/releases/stores/modules/detail'; import createDetailModule from '~/releases/stores/modules/detail';
import RefSelector from '~/ref/components/ref_selector.vue';
const TEST_TAG_NAME = 'test-tag-name';
const TEST_PROJECT_ID = '1234';
const TEST_CREATE_FROM = 'test-create-from';
describe('releases/components/tag_field_new', () => { describe('releases/components/tag_field_new', () => {
let store; let store;
...@@ -16,9 +22,17 @@ describe('releases/components/tag_field_new', () => { ...@@ -16,9 +22,17 @@ describe('releases/components/tag_field_new', () => {
beforeEach(() => { beforeEach(() => {
store = createStore({ store = createStore({
modules: { modules: {
detail: createDetailModule({}), detail: createDetailModule({
projectId: TEST_PROJECT_ID,
}),
}, },
}); });
store.state.detail.createFrom = TEST_CREATE_FROM;
store.state.detail.release = {
tagName: TEST_TAG_NAME,
};
}); });
afterEach(() => { afterEach(() => {
...@@ -26,9 +40,47 @@ describe('releases/components/tag_field_new', () => { ...@@ -26,9 +40,47 @@ describe('releases/components/tag_field_new', () => {
wrapper = null; wrapper = null;
}); });
it('renders a placeholder component', () => { const findTagNameFormGroup = () => wrapper.find('[data-testid="tag-name-field"]');
createComponent(); const findTagNameGlInput = () => findTagNameFormGroup().find(GlFormInput);
const findCreateFromFormGroup = () => wrapper.find('[data-testid="create-from-field"]');
const findCreateFromDropdown = () => findCreateFromFormGroup().find(RefSelector);
describe('"Tag name" field', () => {
beforeEach(createComponent);
it('renders a label', () => {
expect(findTagNameFormGroup().attributes().label).toBe('Tag name');
});
describe('when the user updates the field', () => {
it("updates the store's release.tagName property", () => {
const updatedTagName = 'updated-tag-name';
findTagNameGlInput().vm.$emit('input', updatedTagName);
return wrapper.vm.$nextTick().then(() => {
expect(store.state.detail.release.tagName).toBe(updatedTagName);
});
});
});
});
describe('"Create from" field', () => {
beforeEach(createComponent);
expect(wrapper.exists()).toBe(true); it('renders a label', () => {
expect(findCreateFromFormGroup().attributes().label).toBe('Create from');
});
describe('when the user selects a git ref', () => {
it("updates the store's createFrom property", () => {
const updatedCreateFrom = 'update-create-from';
findCreateFromDropdown().vm.$emit('input', updatedCreateFrom);
return wrapper.vm.$nextTick().then(() => {
expect(store.state.detail.createFrom).toBe(updatedCreateFrom);
});
});
});
}); });
}); });
...@@ -113,6 +113,24 @@ describe('Release detail actions', () => { ...@@ -113,6 +113,24 @@ describe('Release detail actions', () => {
}); });
}); });
describe('updateReleaseTagName', () => {
it(`commits ${types.UPDATE_RELEASE_TAG_NAME} with the updated tag name`, () => {
const newTag = 'updated-tag-name';
return testAction(actions.updateReleaseTagName, newTag, state, [
{ type: types.UPDATE_RELEASE_TAG_NAME, payload: newTag },
]);
});
});
describe('updateCreateFrom', () => {
it(`commits ${types.UPDATE_CREATE_FROM} with the updated ref`, () => {
const newRef = 'my-feature-branch';
return testAction(actions.updateCreateFrom, newRef, state, [
{ type: types.UPDATE_CREATE_FROM, payload: newRef },
]);
});
});
describe('updateReleaseTitle', () => { describe('updateReleaseTitle', () => {
it(`commits ${types.UPDATE_RELEASE_TITLE} with the updated release title`, () => { it(`commits ${types.UPDATE_RELEASE_TITLE} with the updated release title`, () => {
const newTitle = 'The new release title'; const newTitle = 'The new release title';
......
...@@ -56,6 +56,26 @@ describe('Release detail mutations', () => { ...@@ -56,6 +56,26 @@ describe('Release detail mutations', () => {
}); });
}); });
describe(`${types.UPDATE_RELEASE_TAG_NAME}`, () => {
it("updates the release's tag name", () => {
state.release = release;
const newTag = 'updated-tag-name';
mutations[types.UPDATE_RELEASE_TAG_NAME](state, newTag);
expect(state.release.tagName).toBe(newTag);
});
});
describe(`${types.UPDATE_CREATE_FROM}`, () => {
it('updates the ref that the ref will be created from', () => {
state.createFrom = 'main';
const newRef = 'my-feature-branch';
mutations[types.UPDATE_CREATE_FROM](state, newRef);
expect(state.createFrom).toBe(newRef);
});
});
describe(`${types.UPDATE_RELEASE_TITLE}`, () => { describe(`${types.UPDATE_RELEASE_TITLE}`, () => {
it("updates the release's title", () => { it("updates the release's title", () => {
state.release = release; state.release = release;
......
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