Commit f9afaac9 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch 'afontaine/new-environment-action-zone' into 'master'

Re-organize Environment Action Buttons

See merge request gitlab-org/gitlab!70228
parents ae63ee79 528a22f0
......@@ -4,17 +4,15 @@
* Used in the environments table.
*/
import { GlTooltipDirective, GlButton, GlModalDirective } from '@gitlab/ui';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { GlDropdownItem, GlModalDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
export default {
components: {
GlButton,
GlDropdownItem,
},
directives: {
GlTooltip: GlTooltipDirective,
GlModalDirective,
},
props: {
......@@ -28,10 +26,8 @@ export default {
isLoading: false,
};
},
computed: {
title() {
return s__('Environments|Delete environment');
},
i18n: {
title: s__('Environments|Delete environment'),
},
mounted() {
eventHub.$on('deleteEnvironment', this.onDeleteEnvironment);
......@@ -41,7 +37,6 @@ export default {
},
methods: {
onClick() {
this.$root.$emit(BV_HIDE_TOOLTIP, this.$options.deleteEnvironmentTooltipId);
eventHub.$emit('requestDeleteEnvironment', this.environment);
},
onDeleteEnvironment(environment) {
......@@ -50,20 +45,15 @@ export default {
}
},
},
deleteEnvironmentTooltipId: 'delete-environment-button-tooltip',
};
</script>
<template>
<gl-button
v-gl-tooltip="{ id: $options.deleteEnvironmentTooltipId }"
v-gl-modal-directive="'delete-environment-modal'"
<gl-dropdown-item
v-gl-modal-directive.delete-environment-modal
:loading="isLoading"
:title="title"
:aria-label="title"
class="gl-display-none gl-md-display-block"
variant="danger"
category="primary"
icon="remove"
@click="onClick"
/>
>
{{ $options.i18n.title }}
</gl-dropdown-item>
</template>
......@@ -18,22 +18,23 @@ export default {
required: true,
},
},
computed: {
title() {
return s__('Environments|Open live environment');
},
i18n: {
title: s__('Environments|Open live environment'),
open: s__('Environments|Open'),
},
};
</script>
<template>
<gl-button
v-gl-tooltip
:title="title"
:aria-label="title"
:title="$options.i18n.title"
:aria-label="$options.i18n.title"
:href="externalUrl"
class="external-url"
target="_blank"
icon="external-link"
rel="noopener noreferrer nofollow"
/>
>
{{ $options.i18n.open }}
</gl-button>
</template>
<script>
import { GlTooltipDirective, GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
import { GlDropdown, GlTooltipDirective, GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { __, s__, sprintf } from '~/locale';
......@@ -29,6 +29,7 @@ export default {
ActionsComponent,
CommitComponent,
ExternalUrlComponent,
GlDropdown,
GlIcon,
GlLink,
GlSprintf,
......@@ -521,6 +522,10 @@ export default {
return this.model.metrics_path || '';
},
terminalPath() {
return this.model?.terminal_path ?? '';
},
autoStopUrl() {
return this.model.cancel_auto_stop_path || '';
},
......@@ -549,6 +554,15 @@ export default {
tableNameSpacingClass() {
return this.isFolder ? 'section-100' : this.tableData.name.spacing;
},
hasExtraActions() {
return Boolean(
this.canRetry ||
this.canShowAutoStopDate ||
this.monitoringUrl ||
this.terminalPath ||
this.canDeleteEnvironment,
);
},
},
methods: {
......@@ -776,13 +790,6 @@ export default {
role="gridcell"
>
<div class="btn-group table-action-buttons" role="group">
<pin-component
v-if="canShowAutoStopDate"
:auto-stop-url="autoStopUrl"
data-track-action="click_button"
data-track-label="environment_pin"
/>
<external-url-component
v-if="externalURL"
:external-url="externalURL"
......@@ -790,13 +797,6 @@ export default {
data-track-label="environment_url"
/>
<monitoring-button-component
v-if="monitoringUrl"
:monitoring-url="monitoringUrl"
data-track-action="click_button"
data-track-label="environment_monitoring"
/>
<actions-component
v-if="actions.length > 0"
:actions="actions"
......@@ -804,13 +804,22 @@ export default {
data-track-label="environment_actions"
/>
<terminal-button-component
v-if="model && model.terminal_path"
:terminal-path="model.terminal_path"
<stop-component
v-if="canStopEnvironment"
:environment="model"
class="gl-z-index-2"
data-track-action="click_button"
data-track-label="environment_terminal"
data-track-label="environment_stop"
/>
<gl-dropdown
v-if="hasExtraActions"
icon="ellipsis_v"
text-sr-only
:text="__('More actions')"
category="secondary"
no-caret
>
<rollback-component
v-if="canRetry"
:environment="model"
......@@ -820,11 +829,25 @@ export default {
data-track-label="environment_rollback"
/>
<stop-component
v-if="canStopEnvironment"
:environment="model"
<pin-component
v-if="canShowAutoStopDate"
:auto-stop-url="autoStopUrl"
data-track-action="click_button"
data-track-label="environment_stop"
data-track-label="environment_pin"
/>
<monitoring-button-component
v-if="monitoringUrl"
:monitoring-url="monitoringUrl"
data-track-action="click_button"
data-track-label="environment_monitoring"
/>
<terminal-button-component
v-if="terminalPath"
:terminal-path="terminalPath"
data-track-action="click_button"
data-track-label="environment_terminal"
/>
<delete-component
......@@ -833,6 +856,7 @@ export default {
data-track-action="click_button"
data-track-label="environment_delete"
/>
</gl-dropdown>
</div>
</div>
</div>
......
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { GlDropdownItem } from '@gitlab/ui';
import { __ } from '~/locale';
/**
* Renders the Monitoring (Metrics) link in environments table.
*/
export default {
components: {
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
GlDropdownItem,
},
props: {
monitoringUrl: {
......@@ -17,22 +14,11 @@ export default {
required: true,
},
},
computed: {
title() {
return __('Monitoring');
},
},
title: __('Monitoring'),
};
</script>
<template>
<gl-button
v-gl-tooltip
:href="monitoringUrl"
:title="title"
:aria-label="title"
class="monitoring-url gl-display-none gl-sm-display-none gl-md-display-block"
icon="chart"
rel="noopener noreferrer nofollow"
variant="default"
/>
<gl-dropdown-item :href="monitoringUrl" rel="noopener noreferrer nofollow" target="_blank">
{{ $options.title }}
</gl-dropdown-item>
</template>
......@@ -3,17 +3,13 @@
* Renders a prevent auto-stop button.
* Used in environments table.
*/
import { GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import { GlDropdownItem } from '@gitlab/ui';
import { __ } from '~/locale';
import eventHub from '../event_hub';
export default {
components: {
GlIcon,
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
GlDropdownItem,
},
props: {
autoStopUrl: {
......@@ -26,11 +22,11 @@ export default {
eventHub.$emit('cancelAutoStop', this.autoStopUrl);
},
},
title: __('Prevent environment from auto-stopping'),
title: __('Prevent auto-stopping'),
};
</script>
<template>
<gl-button v-gl-tooltip :title="$options.title" :aria-label="$options.title" @click="onPinClick">
<gl-icon name="thumbtack" />
</gl-button>
<gl-dropdown-item @click="onPinClick">
{{ $options.title }}
</gl-dropdown-item>
</template>
......@@ -5,16 +5,15 @@
*
* Makes a post request when the button is clicked.
*/
import { GlTooltipDirective, GlModalDirective, GlButton } from '@gitlab/ui';
import { GlModalDirective, GlDropdownItem } from '@gitlab/ui';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
export default {
components: {
GlButton,
GlDropdownItem,
},
directives: {
GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
},
props: {
......@@ -65,14 +64,7 @@ export default {
};
</script>
<template>
<gl-button
v-gl-tooltip
v-gl-modal.confirm-rollback-modal
class="gl-display-none gl-md-display-block text-secondary"
:loading="isLoading"
:title="title"
:aria-label="title"
:icon="isLastDeployment ? 'repeat' : 'redo'"
@click="onClick"
/>
<gl-dropdown-item v-gl-modal.confirm-rollback-modal @click="onClick">
{{ title }}
</gl-dropdown-item>
</template>
......@@ -23,16 +23,15 @@ export default {
required: true,
},
},
i18n: {
title: s__('Environments|Stop environment'),
stop: s__('Environments|Stop'),
},
data() {
return {
isLoading: false,
};
},
computed: {
title() {
return s__('Environments|Stop environment');
},
},
mounted() {
eventHub.$on('stopEnvironment', this.onStopEnvironment);
},
......@@ -58,11 +57,13 @@ export default {
v-gl-tooltip="{ id: $options.stopEnvironmentTooltipId }"
v-gl-modal-directive="'stop-environment-modal'"
:loading="isLoading"
:title="title"
:aria-label="title"
:title="$options.i18n.title"
:aria-label="$options.i18n.title"
icon="stop"
category="primary"
category="secondary"
variant="danger"
@click="onClick"
/>
>
{{ $options.i18n.stop }}
</gl-button>
</template>
......@@ -3,15 +3,12 @@
* Renders a terminal button to open a web terminal.
* Used in environments table.
*/
import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
import { GlDropdownItem } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
GlDropdownItem,
},
props: {
terminalPath: {
......@@ -25,22 +22,11 @@ export default {
default: false,
},
},
computed: {
title() {
return __('Terminal');
},
},
title: __('Terminal'),
};
</script>
<template>
<a
v-gl-tooltip
:title="title"
:aria-label="title"
:href="terminalPath"
:class="{ disabled: disabled }"
class="btn terminal-button d-none d-md-block text-secondary"
>
<gl-icon name="terminal" />
</a>
<gl-dropdown-item :href="terminalPath" :disabled="disabled">
{{ $options.title }}
</gl-dropdown-item>
</template>
......@@ -35,7 +35,7 @@ To view a list of environments and deployments:
1. On the left sidebar, select **Deployments > Environments**.
The environments are displayed.
![Environments list](img/environments_list.png)
![Environments list](img/environments_list_v14_3.png)
1. To view a list of deployments for an environment, select the environment name,
for example, `staging`.
......@@ -646,9 +646,9 @@ Web terminals:
- Are available to project Maintainers and Owners only.
- Must [be enabled](../../administration/integration/terminal.md).
In the UI, you can view the Web terminal by selecting a **Terminal** button:
In the UI, you can view the Web terminal by selecting **Terminal** from the actions menu:
![Terminal button on environment index](img/environments_terminal_button_on_index_v13_10.png)
![Terminal button on environment index](img/environments_terminal_button_on_index_v14_3.png)
You can also access the terminal button from the page for a specific environment:
......
......@@ -77,7 +77,8 @@ RSpec.describe 'Environments page', :js do
let(:project) { cluster.project }
it 'shows a terminal button' do
expect(page).to have_link(nil, href: terminal_project_environment_path(project, environment))
click_button(_('More actions'))
expect(page).to have_link(_('Terminal'), href: terminal_project_environment_path(project, environment))
end
end
end
......@@ -99,9 +100,8 @@ RSpec.describe 'Environments page', :js do
end
it 'shows re deploy button' do
redeploy_button_selector = %q{button[title="Re-deploy to environment"]}
expect(page).to have_selector(redeploy_button_selector)
click_button(_('More actions'))
expect(page).to have_button(s_('Environments|Re-deploy to environment'))
end
end
end
......@@ -161,6 +161,7 @@ RSpec.describe 'Environments page', :js do
let(:project) { cluster.project }
it 'does not shows a terminal button' do
expect(page).not_to have_button(_('More actions'))
expect(page).not_to have_link(nil, href: terminal_project_environment_path(project, environment))
end
end
......@@ -185,6 +186,7 @@ RSpec.describe 'Environments page', :js do
it 'does not show a re deploy button' do
redeploy_button_selector = %q{button[title="Re-deploy to environment"]}
expect(page).not_to have_button(_('More actions'))
expect(page).not_to have_selector(redeploy_button_selector)
end
end
......
......@@ -13060,6 +13060,9 @@ msgstr ""
msgid "Environments|Note that this action will stop the environment, but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file."
msgstr ""
msgid "Environments|Open"
msgstr ""
msgid "Environments|Open live environment"
msgstr ""
......@@ -25726,6 +25729,9 @@ msgstr ""
msgid "Prevent adding new members to project membership within this group"
msgstr ""
msgid "Prevent auto-stopping"
msgstr ""
msgid "Prevent editing approval rules in projects and merge requests."
msgstr ""
......
......@@ -226,6 +226,7 @@ RSpec.describe 'Environments page', :js do
end
it 'does not show terminal button' do
expect(page).not_to have_button(_('More actions'))
expect(page).not_to have_terminal_button
end
......@@ -273,6 +274,7 @@ RSpec.describe 'Environments page', :js do
let(:role) { :maintainer }
it 'shows the terminal button' do
click_button(_('More actions'))
expect(page).to have_terminal_button
end
end
......@@ -281,6 +283,7 @@ RSpec.describe 'Environments page', :js do
let(:role) { :developer }
it 'does not show terminal button' do
expect(page).not_to have_button(_('More actions'))
expect(page).not_to have_terminal_button
end
end
......@@ -515,7 +518,7 @@ RSpec.describe 'Environments page', :js do
end
def have_terminal_button
have_link(nil, href: terminal_project_environment_path(project, environment))
have_link(_('Terminal'), href: terminal_project_environment_path(project, environment))
end
def visit_environments(project, **opts)
......
import { GlButton } from '@gitlab/ui';
import { GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import DeleteComponent from '~/environments/components/environment_delete.vue';
......@@ -15,7 +15,7 @@ describe('External URL Component', () => {
});
};
const findButton = () => wrapper.find(GlButton);
const findDropdownItem = () => wrapper.find(GlDropdownItem);
beforeEach(() => {
jest.spyOn(window, 'confirm');
......@@ -23,14 +23,15 @@ describe('External URL Component', () => {
createWrapper();
});
it('should render a button to delete the environment', () => {
expect(findButton().exists()).toBe(true);
expect(wrapper.attributes('title')).toEqual('Delete environment');
it('should render a dropdown item to delete the environment', () => {
expect(findDropdownItem().exists()).toBe(true);
expect(wrapper.text()).toEqual('Delete environment');
expect(findDropdownItem().attributes('variant')).toBe('danger');
});
it('emits requestDeleteEnvironment in the event hub when button is clicked', () => {
jest.spyOn(eventHub, '$emit');
findButton().vm.$emit('click');
findDropdownItem().vm.$emit('click');
expect(eventHub.$emit).toHaveBeenCalledWith('requestDeleteEnvironment', wrapper.vm.environment);
});
});
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import MonitoringComponent from '~/environments/components/environment_monitoring.vue';
import { __ } from '~/locale';
describe('Monitoring Component', () => {
let wrapper;
......@@ -8,31 +8,19 @@ describe('Monitoring Component', () => {
const monitoringUrl = 'https://gitlab.com';
const createWrapper = () => {
wrapper = shallowMount(MonitoringComponent, {
wrapper = mountExtended(MonitoringComponent, {
propsData: {
monitoringUrl,
},
});
};
const findButtons = () => wrapper.findAll(GlButton);
const findButtonsByIcon = (icon) =>
findButtons().filter((button) => button.props('icon') === icon);
beforeEach(() => {
createWrapper();
});
describe('computed', () => {
it('title', () => {
expect(wrapper.vm.title).toBe('Monitoring');
});
});
it('should render a link to environment monitoring page', () => {
expect(wrapper.attributes('href')).toEqual(monitoringUrl);
expect(findButtonsByIcon('chart').length).toBe(1);
expect(wrapper.attributes('title')).toBe('Monitoring');
expect(wrapper.attributes('aria-label')).toBe('Monitoring');
const link = wrapper.findByRole('menuitem', { name: __('Monitoring') });
expect(link.attributes('href')).toEqual(monitoringUrl);
});
});
import { GlButton, GlIcon } from '@gitlab/ui';
import { GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PinComponent from '~/environments/components/environment_pin.vue';
import eventHub from '~/environments/event_hub';
......@@ -30,15 +30,15 @@ describe('Pin Component', () => {
wrapper.destroy();
});
it('should render the component with thumbtack icon', () => {
expect(wrapper.find(GlIcon).props('name')).toBe('thumbtack');
it('should render the component with descriptive text', () => {
expect(wrapper.text()).toBe('Prevent auto-stopping');
});
it('should emit onPinClick when clicked', () => {
const eventHubSpy = jest.spyOn(eventHub, '$emit');
const button = wrapper.find(GlButton);
const item = wrapper.find(GlDropdownItem);
button.vm.$emit('click');
item.vm.$emit('click');
expect(eventHubSpy).toHaveBeenCalledWith('cancelAutoStop', autoStopUrl);
});
......
import { GlButton } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils';
import { GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RollbackComponent from '~/environments/components/environment_rollback.vue';
import eventHub from '~/environments/event_hub';
......@@ -7,7 +7,7 @@ describe('Rollback Component', () => {
const retryUrl = 'https://gitlab.com/retry';
it('Should render Re-deploy label when isLastDeployment is true', () => {
const wrapper = mount(RollbackComponent, {
const wrapper = shallowMount(RollbackComponent, {
propsData: {
retryUrl,
isLastDeployment: true,
......@@ -15,11 +15,11 @@ describe('Rollback Component', () => {
},
});
expect(wrapper.element).toHaveSpriteIcon('repeat');
expect(wrapper.text()).toBe('Re-deploy to environment');
});
it('Should render Rollback label when isLastDeployment is false', () => {
const wrapper = mount(RollbackComponent, {
const wrapper = shallowMount(RollbackComponent, {
propsData: {
retryUrl,
isLastDeployment: false,
......@@ -27,7 +27,7 @@ describe('Rollback Component', () => {
},
});
expect(wrapper.element).toHaveSpriteIcon('redo');
expect(wrapper.text()).toBe('Rollback environment');
});
it('should emit a "rollback" event on button click', () => {
......@@ -40,7 +40,7 @@ describe('Rollback Component', () => {
},
},
});
const button = wrapper.find(GlButton);
const button = wrapper.find(GlDropdownItem);
button.vm.$emit('click');
......
import { shallowMount } from '@vue/test-utils';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import TerminalComponent from '~/environments/components/environment_terminal_button.vue';
import { __ } from '~/locale';
describe('Stop Component', () => {
describe('Terminal Component', () => {
let wrapper;
const terminalPath = '/path';
const mountWithProps = (props) => {
wrapper = shallowMount(TerminalComponent, {
wrapper = mountExtended(TerminalComponent, {
propsData: props,
});
};
......@@ -15,17 +16,9 @@ describe('Stop Component', () => {
mountWithProps({ terminalPath });
});
describe('computed', () => {
it('title', () => {
expect(wrapper.vm.title).toEqual('Terminal');
});
});
it('should render a link to open a web terminal with the provided path', () => {
expect(wrapper.element.tagName).toBe('A');
expect(wrapper.attributes('title')).toBe('Terminal');
expect(wrapper.attributes('aria-label')).toBe('Terminal');
expect(wrapper.attributes('href')).toBe(terminalPath);
const link = wrapper.findByRole('menuitem', { name: __('Terminal') });
expect(link.attributes('href')).toBe(terminalPath);
});
it('should render a non-disabled button', () => {
......
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