Skip to content
Snippets Groups Projects
Commit 57f6be00 authored by Adriel Santiago's avatar Adriel Santiago Committed by Fatih Acet
Browse files

Handle external dashboard form submission

Connect frontend UI with backend api for external dashboard link
parent 52b2b325
No related branches found
No related tags found
No related merge requests found
Showing
with 222 additions and 70 deletions
Loading
Loading
@@ -38,7 +38,7 @@ export default {
GlModalDirective,
},
props: {
externalDashboardPath: {
externalDashboardUrl: {
type: String,
required: false,
default: '',
Loading
Loading
@@ -299,10 +299,11 @@ export default {
</gl-modal>
</div>
<gl-button
v-if="externalDashboardPath.length"
v-if="externalDashboardUrl.length"
class="js-external-dashboard-link prepend-left-8"
variant="primary"
:href="externalDashboardPath"
:href="externalDashboardUrl"
target="_blank"
>
{{ __('View full dashboard') }}
<icon name="external-link" />
Loading
Loading
<script>
import { mapState, mapActions } from 'vuex';
import { GlButton, GlFormGroup, GlFormInput, GlLink } from '@gitlab/ui';
 
export default {
Loading
Loading
@@ -8,17 +9,24 @@ export default {
GlFormInput,
GlLink,
},
props: {
externalDashboardPath: {
type: String,
required: false,
default: '',
},
externalDashboardHelpPagePath: {
type: String,
required: true,
computed: {
...mapState([
'externalDashboardHelpPagePath',
'externalDashboardUrl',
'operationsSettingsEndpoint',
]),
userDashboardUrl: {
get() {
return this.externalDashboardUrl;
},
set(url) {
this.setExternalDashboardUrl(url);
},
},
},
methods: {
...mapActions(['setExternalDashboardUrl', 'updateExternalDashboardUrl']),
},
};
</script>
 
Loading
Loading
@@ -45,11 +53,12 @@ export default {
:description="s__('ExternalMetrics|Enter the URL of the dashboard you want to link to')"
>
<gl-form-input
:value="externalDashboardPath"
v-model="userDashboardUrl"
placeholder="https://my-org.gitlab.io/my-dashboards"
@keydown.enter.native.prevent="updateExternalDashboardUrl"
/>
</gl-form-group>
<gl-button variant="success">
<gl-button variant="success" @click="updateExternalDashboardUrl">
{{ __('Save Changes') }}
</gl-button>
</form>
Loading
Loading
import Vue from 'vue';
import store from './store';
import ExternalDashboardForm from './components/external_dashboard.vue';
 
export default () => {
Loading
Loading
@@ -14,13 +15,9 @@ export default () => {
 
return new Vue({
el,
store: store(el.dataset),
render(createElement) {
return createElement(ExternalDashboardForm, {
props: {
...el.dataset,
expanded: false,
},
});
return createElement(ExternalDashboardForm);
},
});
};
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import createFlash from '~/flash';
import { refreshCurrentPage } from '~/lib/utils/url_utility';
import * as mutationTypes from './mutation_types';
export const setExternalDashboardUrl = ({ commit }, url) =>
commit(mutationTypes.SET_EXTERNAL_DASHBOARD_URL, url);
export const updateExternalDashboardUrl = ({ state, dispatch }) =>
axios
.patch(state.operationsSettingsEndpoint, {
project: {
metrics_setting_attributes: {
external_dashboard_url: state.externalDashboardUrl,
},
},
})
.then(() => dispatch('receiveExternalDashboardUpdateSuccess'))
.catch(error => dispatch('receiveExternalDashboardUpdateError', error));
export const receiveExternalDashboardUpdateSuccess = () => {
/**
* The operations_controller currently handles successful requests
* by creating a flash banner messsage to notify the user.
*/
refreshCurrentPage();
};
export const receiveExternalDashboardUpdateError = (_, error) => {
const { response } = error;
const message = response.data && response.data.message ? response.data.message : '';
createFlash(`${__('There was an error saving your changes.')} ${message}`, 'alert');
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import Vue from 'vue';
import Vuex from 'vuex';
import createState from './state';
import * as actions from './actions';
import mutations from './mutations';
Vue.use(Vuex);
export const createStore = initialState =>
new Vuex.Store({
state: createState(initialState),
actions,
mutations,
});
export default createStore;
/* eslint-disable import/prefer-default-export */
export const SET_EXTERNAL_DASHBOARD_URL = 'SET_EXTERNAL_DASHBOARD_URL';
import * as types from './mutation_types';
export default {
[types.SET_EXTERNAL_DASHBOARD_URL](state, url) {
state.externalDashboardUrl = url;
},
};
export default (initialState = {}) => ({
externalDashboardUrl: initialState.externalDashboardUrl || '',
operationsSettingsEndpoint: initialState.operationsSettingsEndpoint,
externalDashboardHelpPagePath: initialState.externalDashboardHelpPagePath,
});
.js-operation-settings{ data: { external_dashboard: { path: metrics_external_dashboard_url,
.js-operation-settings{ data: { operations_settings_endpoint: project_settings_operations_path(@project),
external_dashboard: { url: metrics_external_dashboard_url,
help_page_path: help_page_path('user/project/operations/link_to_external_dashboard') } } }
import { shallowMount } from '@vue/test-utils';
import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import { GlButton, GlLink, GlFormGroup, GlFormInput } from '@gitlab/ui';
import ExternalDashboard from '~/operation_settings/components/external_dashboard.vue';
import store from '~/operation_settings/store';
import axios from '~/lib/utils/axios_utils';
import { refreshCurrentPage } from '~/lib/utils/url_utility';
import createFlash from '~/flash';
import { TEST_HOST } from 'helpers/test_constants';
 
jest.mock('~/lib/utils/axios_utils');
jest.mock('~/lib/utils/url_utility');
jest.mock('~/flash');
describe('operation settings external dashboard component', () => {
let wrapper;
const externalDashboardPath = `http://mock-external-domain.com/external/dashboard/path`;
const operationsSettingsEndpoint = `${TEST_HOST}/mock/ops/settings/endpoint`;
const externalDashboardUrl = `http://mock-external-domain.com/external/dashboard/url`;
const externalDashboardHelpPagePath = `${TEST_HOST}/help/page/path`;
beforeEach(() => {
wrapper = shallowMount(ExternalDashboard, {
propsData: {
externalDashboardPath,
externalDashboardHelpPagePath,
const localVue = createLocalVue();
const mountComponent = (shallow = true) => {
const config = [
ExternalDashboard,
{
localVue,
store: store({
operationsSettingsEndpoint,
externalDashboardUrl,
externalDashboardHelpPagePath,
}),
},
});
];
wrapper = shallow ? shallowMount(...config) : mount(...config);
};
afterEach(() => {
if (wrapper.destroy) {
wrapper.destroy();
}
axios.patch.mockReset();
refreshCurrentPage.mockReset();
createFlash.mockReset();
});
 
it('renders header text', () => {
mountComponent();
expect(wrapper.find('.js-section-header').text()).toBe('External Dashboard');
});
 
Loading
Loading
@@ -33,6 +58,7 @@ describe('operation settings external dashboard component', () => {
let subHeader;
 
beforeEach(() => {
mountComponent();
subHeader = wrapper.find('.js-section-sub-header');
});
 
Loading
Loading
@@ -51,57 +77,87 @@ describe('operation settings external dashboard component', () => {
});
 
describe('form', () => {
let form;
describe('input label', () => {
let formGroup;
 
beforeEach(() => {
form = wrapper.find('form');
});
beforeEach(() => {
mountComponent();
formGroup = wrapper.find(GlFormGroup);
});
 
describe('external dashboard url', () => {
describe('input label', () => {
let formGroup;
it('uses label text', () => {
expect(formGroup.attributes().label).toBe('Full dashboard URL');
});
 
beforeEach(() => {
formGroup = form.find(GlFormGroup);
});
it('uses description text', () => {
expect(formGroup.attributes().description).toBe(
'Enter the URL of the dashboard you want to link to',
);
});
});
 
it('uses label text', () => {
expect(formGroup.attributes().label).toBe('Full dashboard URL');
});
describe('input field', () => {
let input;
 
it('uses description text', () => {
expect(formGroup.attributes().description).toBe(
'Enter the URL of the dashboard you want to link to',
);
});
beforeEach(() => {
mountComponent();
input = wrapper.find(GlFormInput);
});
 
describe('input field', () => {
let input;
beforeEach(() => {
input = form.find(GlFormInput);
});
it('defaults to externalDashboardUrl', () => {
expect(input.attributes().value).toBe(externalDashboardUrl);
});
 
it('defaults to externalDashboardPath prop', () => {
expect(input.attributes().value).toBe(externalDashboardPath);
});
it('uses a placeholder', () => {
expect(input.attributes().placeholder).toBe('https://my-org.gitlab.io/my-dashboards');
});
});
 
it('uses a placeholder', () => {
expect(input.attributes().placeholder).toBe('https://my-org.gitlab.io/my-dashboards');
});
describe('submit button', () => {
const endpointRequest = [
operationsSettingsEndpoint,
{
project: {
metrics_setting_attributes: {
external_dashboard_url: externalDashboardUrl,
},
},
},
];
it('renders button label', () => {
mountComponent();
const submit = wrapper.find(GlButton);
expect(submit.text()).toBe('Save Changes');
});
 
describe('submit button', () => {
let submit;
it('submits form on click', () => {
mountComponent(false);
axios.patch.mockResolvedValue();
wrapper.find(GlButton).trigger('click');
expect(axios.patch).toHaveBeenCalledWith(...endpointRequest);
 
beforeEach(() => {
submit = form.find(GlButton);
});
return wrapper.vm.$nextTick().then(() => expect(refreshCurrentPage).toHaveBeenCalled());
});
 
it('renders button label', () => {
expect(submit.text()).toBe('Save Changes');
});
it('creates flash banner on error', () => {
mountComponent(false);
const message = 'mockErrorMessage';
axios.patch.mockRejectedValue({ response: { data: { message } } });
wrapper.find(GlButton).trigger('click');
expect(axios.patch).toHaveBeenCalledWith(...endpointRequest);
return wrapper.vm
.$nextTick()
.then(jest.runAllTicks)
.then(() =>
expect(createFlash).toHaveBeenCalledWith(
`There was an error saving your changes. ${message}`,
'alert',
),
);
});
});
});
Loading
Loading
import mutations from '~/operation_settings/store/mutations';
import createState from '~/operation_settings/store/state';
describe('operation settings mutations', () => {
let localState;
beforeEach(() => {
localState = createState();
});
describe('SET_EXTERNAL_DASHBOARD_URL', () => {
it('sets externalDashboardUrl', () => {
const mockUrl = 'mockUrl';
mutations.SET_EXTERNAL_DASHBOARD_URL(localState, mockUrl);
expect(localState.externalDashboardUrl).toBe(mockUrl);
});
});
});
Loading
Loading
@@ -393,7 +393,7 @@ describe('Dashboard', () => {
hasMetrics: true,
showPanels: false,
showTimeWindowDropdown: false,
externalDashboardPath: '/mockPath',
externalDashboardUrl: '/mockUrl',
},
store,
});
Loading
Loading
@@ -419,7 +419,7 @@ describe('Dashboard', () => {
hasMetrics: true,
showPanels: false,
showTimeWindowDropdown: false,
externalDashboardPath: '',
externalDashboardUrl: '',
},
store,
});
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment