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

Remove negative-margin-top class

Users can click on "Run Pipeline" to start their pipelines on
merge requests.

The layout was achieved by repurposing the "Run pipeline" button
so it shows in the right place in mobile and desktop by adding
some negative margins.

Now this is done by adding a slot to place the button inside the table.
parent 24b3ceda
<script>
import { GlButton, GlLoadingIcon, GlModal, GlLink } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import PipelinesService from '~/pipelines/services/pipelines_service';
import PipelineStore from '~/pipelines/stores/pipelines_store';
import pipelinesMixin from '~/pipelines/mixins/pipelines';
......@@ -126,16 +125,6 @@ export default {
(latest.flags.detached_merge_request_pipeline || latest.flags.merge_request_pipeline)
);
},
/**
* When we are on Desktop and the button is visible
* we need to add a negative margin to the table
* to make it inline with the button
*
* @returns {Boolean}
*/
shouldAddNegativeMargin() {
return this.canRenderPipelineButton && bp.isDesktop();
},
},
created() {
this.service = new PipelinesService(this.endpoint);
......@@ -205,18 +194,41 @@ export default {
/>
<div v-else-if="shouldRenderTable" class="table-holder">
<div v-if="canRenderPipelineButton" class="nav justify-content-end">
<gl-button
v-if="canRenderPipelineButton"
block
class="gl-mt-3 gl-mb-0 gl-display-md-none"
variant="success"
class="js-run-mr-pipeline gl-mt-3 btn-wide-on-xs"
:disabled="state.isRunningMergeRequestPipeline"
data-testid="run_pipeline_button_mobile"
:loading="state.isRunningMergeRequestPipeline"
@click="tryRunPipeline"
>
<gl-loading-icon v-if="state.isRunningMergeRequestPipeline" inline />
{{ s__('Pipelines|Run Pipeline') }}
</gl-button>
<pipelines-table-component
:pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
>
<template #table-header-actions>
<div v-if="canRenderPipelineButton" class="gl-text-right">
<gl-button
variant="success"
data-testid="run_pipeline_button"
:loading="state.isRunningMergeRequestPipeline"
@click="tryRunPipeline"
>
{{ s__('Pipelines|Run Pipeline') }}
</gl-button>
</div>
</template>
</pipelines-table-component>
</div>
<gl-modal
v-if="canRenderPipelineButton"
:id="modalId"
ref="modal"
:modal-id="modalId"
......@@ -241,9 +253,7 @@ export default {
</p>
<p>
{{
s__(
'Pipelines|If you are unsure, please ask a project maintainer to review it for you.',
)
s__('Pipelines|If you are unsure, please ask a project maintainer to review it for you.')
}}
</p>
<gl-link
......@@ -253,16 +263,6 @@ export default {
{{ s__('Pipelines|More Information') }}
</gl-link>
</gl-modal>
</div>
<pipelines-table-component
:pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
:class="{ 'negative-margin-top': shouldAddNegativeMargin }"
/>
</div>
<table-pagination
v-if="shouldRenderPagination"
......
......@@ -91,6 +91,10 @@ export default {
<div class="table-section section-15 js-pipeline-stages pipeline-stages" role="rowheader">
{{ s__('Pipeline|Stages') }}
</div>
<div class="table-section section-15" role="rowheader"></div>
<div class="table-section section-20" role="rowheader">
<slot name="table-header-actions"></slot>
</div>
</div>
<pipelines-table-row-component
v-for="model in pipelines"
......
......@@ -417,12 +417,6 @@
}
}
@include media-breakpoint-down(xs) {
.btn-wide-on-xs {
width: 100%;
}
}
.btn-blank {
padding: 0;
background: transparent;
......
......@@ -819,7 +819,6 @@ $pipeline-dropdown-line-height: 20px;
$pipeline-dropdown-status-icon-size: 18px;
$ci-action-dropdown-button-size: 24px;
$ci-action-dropdown-svg-size: 12px;
$pipelines-table-header-height: 40px;
/*
CI variable lists
......
......@@ -26,10 +26,6 @@
}
.pipelines {
.negative-margin-top {
margin-top: -$pipelines-table-header-height;
}
.stage {
max-width: 90px;
width: 90px;
......
......@@ -50,7 +50,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
wait_for_requests
expect(page.find('.js-run-mr-pipeline')).to have_text('Run Pipeline')
expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run Pipeline')
end
end
......@@ -66,7 +66,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js do
wait_for_requests
expect(page.find('.js-run-mr-pipeline')).to have_text('Run Pipeline')
expect(page.find('[data-testid="run_pipeline_button"]')).to have_text('Run Pipeline')
end
end
end
......
......@@ -21,6 +21,10 @@ describe('Pipelines table in Commits and Merge requests', () => {
preloadFixtures(jsonFixtureName);
const findRunPipelineBtn = () => vm.$el.querySelector('[data-testid="run_pipeline_button"]');
const findRunPipelineBtnMobile = () =>
vm.$el.querySelector('[data-testid="run_pipeline_button_mobile"]');
beforeEach(() => {
mock = new MockAdapter(axios);
......@@ -131,7 +135,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
vm = mountComponent(PipelinesTable, { ...props });
setImmediate(() => {
expect(vm.$el.querySelector('.js-run-mr-pipeline')).not.toBeNull();
expect(findRunPipelineBtn()).not.toBeNull();
expect(findRunPipelineBtnMobile()).not.toBeNull();
done();
});
});
......@@ -147,7 +152,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
vm = mountComponent(PipelinesTable, { ...props });
setImmediate(() => {
expect(vm.$el.querySelector('.js-run-mr-pipeline')).toBeNull();
expect(findRunPipelineBtn()).toBeNull();
expect(findRunPipelineBtnMobile()).toBeNull();
done();
});
});
......@@ -157,7 +163,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
const findModal = () =>
document.querySelector('#create-pipeline-for-fork-merge-request-modal');
beforeEach(() => {
beforeEach(done => {
pipelineCopy.flags.detached_merge_request_pipeline = true;
mock.onGet('endpoint.json').reply(200, [pipelineCopy]);
......@@ -168,25 +174,48 @@ describe('Pipelines table in Commits and Merge requests', () => {
projectId: '5',
mergeRequestId: 3,
});
});
it('updates the loading state', done => {
jest.spyOn(Api, 'postMergeRequestPipeline').mockReturnValue(Promise.resolve());
setImmediate(() => {
vm.$el.querySelector('.js-run-mr-pipeline').click();
done();
});
});
it('on desktop, shows a loading button', done => {
findRunPipelineBtn().click();
vm.$nextTick(() => {
expect(findModal()).toBeNull();
expect(vm.state.isRunningMergeRequestPipeline).toBe(true);
expect(findRunPipelineBtn().disabled).toBe(true);
expect(findRunPipelineBtn().querySelector('.gl-spinner')).not.toBeNull();
setImmediate(() => {
expect(vm.state.isRunningMergeRequestPipeline).toBe(false);
expect(findRunPipelineBtn().disabled).toBe(false);
expect(findRunPipelineBtn().querySelector('.gl-spinner')).toBeNull();
done();
});
});
});
it('on mobile, shows a loading button', done => {
findRunPipelineBtnMobile().click();
vm.$nextTick(() => {
expect(findModal()).toBeNull();
expect(findModal()).toBeNull();
expect(findRunPipelineBtn().querySelector('.gl-spinner')).not.toBeNull();
setImmediate(() => {
expect(findRunPipelineBtn().disabled).toBe(false);
expect(findRunPipelineBtn().querySelector('.gl-spinner')).toBeNull();
done();
});
});
});
});
......@@ -194,7 +223,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
const findModal = () =>
document.querySelector('#create-pipeline-for-fork-merge-request-modal');
beforeEach(() => {
beforeEach(done => {
pipelineCopy.flags.detached_merge_request_pipeline = true;
mock.onGet('endpoint.json').reply(200, [pipelineCopy]);
......@@ -207,19 +236,30 @@ describe('Pipelines table in Commits and Merge requests', () => {
sourceProjectFullPath: 'test/parent-project',
targetProjectFullPath: 'test/fork-project',
});
});
it('shows a security warning modal', done => {
jest.spyOn(Api, 'postMergeRequestPipeline').mockReturnValue(Promise.resolve());
setImmediate(() => {
vm.$el.querySelector('.js-run-mr-pipeline').click();
done();
});
});
it('on desktop, shows a security warning modal', done => {
findRunPipelineBtn().click();
vm.$nextTick(() => {
expect(findModal()).not.toBeNull();
done();
});
});
it('on mobile, shows a security warning modal', done => {
findRunPipelineBtnMobile().click();
vm.$nextTick(() => {
expect(findModal()).not.toBeNull();
done();
});
});
});
});
......
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