Commit 3884a644 authored by Zack Cuddy's avatar Zack Cuddy Committed by Jose Ivan Vargas

Geo - Clean Up Geo Node Actions

This change removes a bit of technical debt from Geo Actions.
It removes some legacy css classes, old components and fixes an
alignment issue.
parent 8c6cc649
<script> <script>
import { GlDeprecatedButton, GlTooltipDirective } from '@gitlab/ui'; import { GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import { NODE_ACTIONS } from '../constants'; import { NODE_ACTIONS } from '../constants';
import Icon from '~/vue_shared/components/icon.vue';
export default { export default {
components: { components: {
Icon, GlIcon,
GlDeprecatedButton, GlButton,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -87,48 +86,53 @@ export default { ...@@ -87,48 +86,53 @@ export default {
</script> </script>
<template> <template>
<div class="d-flex align-items-center justify-content-end geo-node-actions"> <div
<a data-testid="nodeActions"
class="geo-node-actions gl-display-flex gl-align-items-center gl-justify-content-end gl-flex-direction-column gl-sm-flex-direction-row"
>
<gl-button
v-if="isSecondaryNode" v-if="isSecondaryNode"
:href="node.geoProjectsUrl" :href="node.geoProjectsUrl"
class="btn btn-sm mx-1 sm-column-spacing" class="gl-mx-2 gl-mt-5 gl-sm-mt-0"
target="_blank" target="_blank"
> >
<icon v-if="!node.current" name="external-link" /> {{ __('Open projects') }} <span class="gl-display-flex gl-align-items-center">
</a> <gl-icon v-if="!node.current" name="external-link" class="gl-mr-2" />
{{ __('Open projects') }}
</span>
</gl-button>
<template v-if="nodeActionsAllowed"> <template v-if="nodeActionsAllowed">
<gl-deprecated-button <gl-button v-if="nodeMissingOauth" class="gl-mx-2 gl-mt-5 gl-sm-mt-0" @click="onRepairNode">
v-if="nodeMissingOauth"
class="btn btn-sm btn-default mx-1 sm-column-spacing"
@click="onRepairNode"
>
{{ s__('Repair authentication') }} {{ s__('Repair authentication') }}
</gl-deprecated-button> </gl-button>
<a v-if="nodeEditAllowed" :href="node.editPath" class="btn btn-sm mx-1 sm-column-spacing"> <gl-button v-if="nodeEditAllowed" :href="node.editPath" class="gl-mx-2 gl-mt-5 gl-sm-mt-0">
{{ __('Edit') }} {{ __('Edit') }}
</a> </gl-button>
<gl-deprecated-button <gl-button
v-if="isSecondaryNode" v-if="isSecondaryNode"
class="btn btn-sm btn-danger mx-1 sm-column-spacing" data-testid="removeButton"
variant="danger"
class="gl-mx-2 gl-mt-5 gl-sm-mt-0"
:disabled="!nodeRemovalAllowed" :disabled="!nodeRemovalAllowed"
@click="onRemoveSecondaryNode" @click="onRemoveSecondaryNode"
> >
{{ __('Remove') }} {{ __('Remove') }}
</gl-deprecated-button> </gl-button>
<div <div
v-gl-tooltip.hover v-gl-tooltip.hover
name="disabledRemovalTooltip" name="disabledRemovalTooltip"
class="mx-1 sm-column-spacing" class="gl-mx-2 gl-mt-5 gl-sm-mt-0"
:title="disabledRemovalTooltip" :title="disabledRemovalTooltip"
> >
<gl-deprecated-button <gl-button
v-if="!isSecondaryNode" v-if="!isSecondaryNode"
class="btn btn-sm btn-danger w-100" variant="danger"
class="gl-w-full"
:disabled="!nodeRemovalAllowed" :disabled="!nodeRemovalAllowed"
@click="onRemovePrimaryNode" @click="onRemovePrimaryNode"
> >
{{ __('Remove') }} {{ __('Remove') }}
</gl-deprecated-button> </gl-button>
</div> </div>
</template> </template>
</div> </div>
......
...@@ -74,7 +74,7 @@ export default { ...@@ -74,7 +74,7 @@ export default {
<template> <template>
<div class="row-fluid clearfix py-3 primary-section"> <div class="row-fluid clearfix py-3 primary-section">
<div class="col-md-12"> <div class="col-md-12">
<div class="d-flex geo-node-actions-container"> <div class="gl-display-flex gl-flex-wrap gl-flex-direction-column gl-sm-flex-direction-row">
<div data-testid="nodeUrl" class="d-flex flex-column"> <div data-testid="nodeUrl" class="d-flex flex-column">
<span class="gl-text-gray-700">{{ s__('GeoNodes|Node URL') }}</span> <span class="gl-text-gray-700">{{ s__('GeoNodes|Node URL') }}</span>
<gl-link <gl-link
......
@media (max-width: map-get($grid-breakpoints, sm)) { @media (max-width: $breakpoint-sm) {
.geo-node-actions { .geo-node-actions {
flex-direction: column;
margin: 0 1rem; margin: 0 1rem;
.sm-column-spacing { > * {
width: 100%; width: 100%;
margin-top: 1rem;
} }
} }
.geo-node-actions-container {
flex-direction: column;
}
} }
.project-card-errors { .project-card-errors {
......
---
title: Geo - Fix Button Icon Alignment
merge_request: 36867
author:
type: fixed
import Vue from 'vue'; import { shallowMount } from '@vue/test-utils';
import GeoNodeActionsComponent from 'ee/geo_nodes/components/geo_node_actions.vue';
import geoNodeActionsComponent from 'ee/geo_nodes/components/geo_node_actions.vue'; import { GlButton } from '@gitlab/ui';
import mountComponent from 'helpers/vue_mount_component_helper';
import eventHub from 'ee/geo_nodes/event_hub'; import eventHub from 'ee/geo_nodes/event_hub';
import { NODE_ACTIONS } from 'ee/geo_nodes/constants'; import { NODE_ACTIONS } from 'ee/geo_nodes/constants';
import { mockNodes } from '../mock_data'; import { mockNodes } from '../mock_data';
jest.mock('ee/geo_nodes/event_hub'); jest.mock('ee/geo_nodes/event_hub');
const createComponent = (
node = mockNodes[0],
nodeEditAllowed = true,
nodeActionsAllowed = true,
nodeRemovalAllowed = true,
nodeMissingOauth = false,
) => {
const Component = Vue.extend(geoNodeActionsComponent);
return mountComponent(Component, {
node,
nodeEditAllowed,
nodeActionsAllowed,
nodeRemovalAllowed,
nodeMissingOauth,
});
};
describe('GeoNodeActionsComponent', () => { describe('GeoNodeActionsComponent', () => {
let vm; let wrapper;
beforeEach(() => { const defaultProps = {
vm = createComponent(); node: mockNodes[0],
nodeEditAllowed: true,
nodeActionsAllowed: true,
nodeRemovalAllowed: true,
nodeMissingOauth: false,
};
const createComponent = (props = {}) => {
wrapper = shallowMount(GeoNodeActionsComponent, {
propsData: {
...defaultProps,
...props,
},
}); });
};
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
wrapper = null;
}); });
const findGeoNodeActionsComponent = () => wrapper.find('[data-testid="nodeActions"]');
const findNodeActions = () => wrapper.findAll(GlButton);
const findRemoveButton = () => wrapper.find('[data-testid="removeButton"]');
describe('computed', () => { describe('computed', () => {
describe('disabledRemovalTooltip', () => { describe('disabledRemovalTooltip', () => {
describe.each` describe.each`
...@@ -45,11 +44,11 @@ describe('GeoNodeActionsComponent', () => { ...@@ -45,11 +44,11 @@ describe('GeoNodeActionsComponent', () => {
${false} | ${'Cannot remove a primary node if there is a secondary node'} ${false} | ${'Cannot remove a primary node if there is a secondary node'}
`('when nodeRemovalAllowed is $nodeRemovalAllowed', ({ nodeRemovalAllowed, tooltip }) => { `('when nodeRemovalAllowed is $nodeRemovalAllowed', ({ nodeRemovalAllowed, tooltip }) => {
beforeEach(() => { beforeEach(() => {
vm = createComponent(mockNodes[0], true, true, nodeRemovalAllowed, false); createComponent({ nodeRemovalAllowed });
}); });
it('renders the correct tooltip', () => { it('renders the correct tooltip', () => {
const tip = vm.$el.querySelector('div[name=disabledRemovalTooltip]'); const tip = wrapper.vm.$el.querySelector('div[name=disabledRemovalTooltip]');
expect(tip.title).toBe(tooltip); expect(tip.title).toBe(tooltip);
}); });
}); });
...@@ -57,13 +56,17 @@ describe('GeoNodeActionsComponent', () => { ...@@ -57,13 +56,17 @@ describe('GeoNodeActionsComponent', () => {
}); });
describe('methods', () => { describe('methods', () => {
beforeEach(() => {
createComponent();
});
describe('onRemovePrimaryNode', () => { describe('onRemovePrimaryNode', () => {
it('emits showNodeActionModal with actionType `remove`, node reference, modalKind, modalMessage, modalActionLabel, and modalTitle', () => { it('emits showNodeActionModal with actionType `remove`, node reference, modalKind, modalMessage, modalActionLabel, and modalTitle', () => {
vm.onRemovePrimaryNode(); wrapper.vm.onRemovePrimaryNode();
expect(eventHub.$emit).toHaveBeenCalledWith('showNodeActionModal', { expect(eventHub.$emit).toHaveBeenCalledWith('showNodeActionModal', {
actionType: NODE_ACTIONS.REMOVE, actionType: NODE_ACTIONS.REMOVE,
node: vm.node, node: wrapper.vm.node,
modalKind: 'danger', modalKind: 'danger',
modalMessage: modalMessage:
'Removing a Geo primary node stops the synchronization to all nodes. Are you sure?', 'Removing a Geo primary node stops the synchronization to all nodes. Are you sure?',
...@@ -75,11 +78,11 @@ describe('GeoNodeActionsComponent', () => { ...@@ -75,11 +78,11 @@ describe('GeoNodeActionsComponent', () => {
describe('onRemoveSecondaryNode', () => { describe('onRemoveSecondaryNode', () => {
it('emits showNodeActionModal with actionType `remove`, node reference, modalKind, modalMessage, modalActionLabel, and modalTitle', () => { it('emits showNodeActionModal with actionType `remove`, node reference, modalKind, modalMessage, modalActionLabel, and modalTitle', () => {
vm.onRemoveSecondaryNode(); wrapper.vm.onRemoveSecondaryNode();
expect(eventHub.$emit).toHaveBeenCalledWith('showNodeActionModal', { expect(eventHub.$emit).toHaveBeenCalledWith('showNodeActionModal', {
actionType: NODE_ACTIONS.REMOVE, actionType: NODE_ACTIONS.REMOVE,
node: vm.node, node: wrapper.vm.node,
modalKind: 'danger', modalKind: 'danger',
modalMessage: modalMessage:
'Removing a Geo secondary node stops the synchronization to that node. Are you sure?', 'Removing a Geo secondary node stops the synchronization to that node. Are you sure?',
...@@ -91,41 +94,45 @@ describe('GeoNodeActionsComponent', () => { ...@@ -91,41 +94,45 @@ describe('GeoNodeActionsComponent', () => {
describe('onRepairNode', () => { describe('onRepairNode', () => {
it('emits `repairNode` event with node reference', () => { it('emits `repairNode` event with node reference', () => {
vm.onRepairNode(); wrapper.vm.onRepairNode();
expect(eventHub.$emit).toHaveBeenCalledWith('repairNode', vm.node); expect(eventHub.$emit).toHaveBeenCalledWith('repairNode', wrapper.vm.node);
}); });
}); });
}); });
describe('template', () => { describe('template', () => {
beforeEach(() => {
createComponent();
});
it('renders container elements correctly', () => { it('renders container elements correctly', () => {
expect(vm.$el.classList.contains('geo-node-actions')).toBe(true); expect(findGeoNodeActionsComponent().exists()).toBeTruthy();
expect(vm.$el.querySelectorAll('.btn-sm').length).not.toBe(0); expect(findNodeActions()).not.toHaveLength(0);
}); });
describe.each` describe.each`
nodeRemovalAllowed | buttonDisabled nodeRemovalAllowed | buttonDisabled
${false} | ${true} ${false} | ${'true'}
${true} | ${false} ${true} | ${undefined}
`( `(`Remove Button`, ({ nodeRemovalAllowed, buttonDisabled }) => {
`when nodeRemovalAllowed is $nodeRemovalAllowed`,
({ nodeRemovalAllowed, buttonDisabled }) => {
let removeButton;
beforeEach(() => { beforeEach(() => {
vm = createComponent(mockNodes[0], true, true, nodeRemovalAllowed, false); createComponent({ node: mockNodes[1], nodeRemovalAllowed });
removeButton = vm.$el.querySelector('.btn-danger');
}); });
describe(`when nodeRemovalAllowed is ${nodeRemovalAllowed}`, () => {
it('has the correct button text', () => { it('has the correct button text', () => {
expect(removeButton.innerText.trim()).toBe('Remove'); expect(
findRemoveButton()
.text()
.trim(),
).toBe('Remove');
}); });
it(`the button's disabled attribute should be ${buttonDisabled}`, () => { it(`the button's disabled attribute should be ${buttonDisabled}`, () => {
expect(removeButton.disabled).toBe(buttonDisabled); expect(findRemoveButton().attributes('disabled')).toBe(buttonDisabled);
});
});
}); });
},
);
}); });
}); });
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