Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-13 18:09:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-13 18:09:21 +0300
commitc36152ff8c41fad2f413f253eb7ac5c927e47c56 (patch)
treebbf300da207de3e8bbf272d44111ceedb18f5833 /app/assets/javascripts/ci_variable_list
parent286fe61013674fe2d245ffc8d2233baf09923e70 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/ci_variable_list')
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue118
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_popover.vue55
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue104
-rw-r--r--app/assets/javascripts/ci_variable_list/constants.js16
-rw-r--r--app/assets/javascripts/ci_variable_list/store/mutations.js8
-rw-r--r--app/assets/javascripts/ci_variable_list/store/state.js8
-rw-r--r--app/assets/javascripts/ci_variable_list/store/utils.js24
7 files changed, 250 insertions, 83 deletions
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
index 3e58fc40755..637f0237b63 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
@@ -7,6 +7,7 @@ import {
GlFormSelect,
GlFormGroup,
GlFormInput,
+ GlFormTextarea,
GlFormCheckbox,
GlLink,
GlIcon,
@@ -19,6 +20,7 @@ export default {
GlFormSelect,
GlFormGroup,
GlFormInput,
+ GlFormTextarea,
GlFormCheckbox,
GlLink,
GlIcon,
@@ -34,17 +36,29 @@ export default {
'maskableRegex',
]),
canSubmit() {
+ if (this.variableData.masked && this.maskedState === false) {
+ return false;
+ }
return this.variableData.key !== '' && this.variableData.secret_value !== '';
},
canMask() {
const regex = RegExp(this.maskableRegex);
return regex.test(this.variableData.secret_value);
},
+ displayMaskedError() {
+ return !this.canMask && this.variableData.masked && this.variableData.secret_value !== '';
+ },
+ maskedState() {
+ if (this.displayMaskedError) {
+ return false;
+ }
+ return null;
+ },
variableData() {
return this.variableBeingEdited || this.variable;
},
modalActionText() {
- return this.variableBeingEdited ? __('Update Variable') : __('Add variable');
+ return this.variableBeingEdited ? __('Update variable') : __('Add variable');
},
primaryAction() {
return {
@@ -52,11 +66,23 @@ export default {
attributes: { variant: 'success', disabled: !this.canSubmit },
};
},
+ deleteAction() {
+ if (this.variableBeingEdited) {
+ return {
+ text: __('Delete variable'),
+ attributes: { variant: 'danger', category: 'secondary' },
+ };
+ }
+ return null;
+ },
cancelAction() {
return {
text: __('Cancel'),
};
},
+ maskedFeedback() {
+ return __('This variable can not be masked');
+ },
},
methods: {
...mapActions([
@@ -65,6 +91,7 @@ export default {
'resetEditing',
'displayInputValue',
'clearModal',
+ 'deleteVariable',
]),
updateOrAddVariable() {
if (this.variableBeingEdited) {
@@ -89,74 +116,93 @@ export default {
:modal-id="$options.modalId"
:title="modalActionText"
:action-primary="primaryAction"
+ :action-secondary="deleteAction"
:action-cancel="cancelAction"
@ok="updateOrAddVariable"
@hidden="resetModalHandler"
+ @secondary="deleteVariable(variableBeingEdited)"
>
<form>
- <gl-form-group label="Type" label-for="ci-variable-type">
- <gl-form-select
- id="ci-variable-type"
- v-model="variableData.variable_type"
- :options="typeOptions"
+ <gl-form-group :label="__('Key')" label-for="ci-variable-key">
+ <gl-form-input
+ id="ci-variable-key"
+ v-model="variableData.key"
+ data-qa-selector="variable_key"
+ />
+ </gl-form-group>
+
+ <gl-form-group
+ :label="__('Value')"
+ label-for="ci-variable-value"
+ :state="maskedState"
+ :invalid-feedback="maskedFeedback"
+ >
+ <gl-form-textarea
+ id="ci-variable-value"
+ v-model="variableData.secret_value"
+ rows="3"
+ max-rows="6"
+ data-qa-selector="variable_value"
/>
</gl-form-group>
<div class="d-flex">
- <gl-form-group label="Key" label-for="ci-variable-key" class="w-50 append-right-15">
- <gl-form-input
- id="ci-variable-key"
- v-model="variableData.key"
- type="text"
- data-qa-selector="variable_key"
+ <gl-form-group
+ :label="__('Type')"
+ label-for="ci-variable-type"
+ class="w-50 append-right-15"
+ :class="{ 'w-100': isGroup }"
+ >
+ <gl-form-select
+ id="ci-variable-type"
+ v-model="variableData.variable_type"
+ :options="typeOptions"
/>
</gl-form-group>
- <gl-form-group label="Value" label-for="ci-variable-value" class="w-50">
- <gl-form-input
- id="ci-variable-value"
- v-model="variableData.secret_value"
- type="text"
- data-qa-selector="variable_value"
+ <gl-form-group
+ v-if="!isGroup"
+ :label="__('Environment scope')"
+ label-for="ci-variable-env"
+ class="w-50"
+ >
+ <gl-form-select
+ id="ci-variable-env"
+ v-model="variableData.environment_scope"
+ :options="environments"
/>
</gl-form-group>
</div>
- <gl-form-group v-if="!isGroup" label="Environment scope" label-for="ci-variable-env">
- <gl-form-select
- id="ci-variable-env"
- v-model="variableData.environment_scope"
- :options="environments"
- />
- </gl-form-group>
-
- <gl-form-group label="Flags" label-for="ci-variable-flags">
+ <gl-form-group :label="__('Flags')" label-for="ci-variable-flags">
<gl-form-checkbox v-model="variableData.protected" class="mb-0">
{{ __('Protect variable') }}
<gl-link href="/help/ci/variables/README#protected-environment-variables">
<gl-icon name="question" :size="12" />
</gl-link>
- <p class="prepend-top-4 clgray">
- {{ __('Allow variables to run on protected branches and tags.') }}
+ <p class="prepend-top-4 text-secondary">
+ {{ __('Export variable to pipelines running on protected branches and tags only.') }}
</p>
</gl-form-checkbox>
<gl-form-checkbox
ref="masked-ci-variable"
v-model="variableData.masked"
- :disabled="!canMask"
data-qa-selector="variable_masked"
>
{{ __('Mask variable') }}
<gl-link href="/help/ci/variables/README#masked-variables">
<gl-icon name="question" :size="12" />
</gl-link>
- <p class="prepend-top-4 append-bottom-0 clgray">
- {{
- __(
- 'Variables will be masked in job logs. Requires values to meet regular expression requirements.',
- )
- }}
+ <p class="prepend-top-4 append-bottom-0 text-secondary">
+ {{ __('Variable will be masked in job logs.') }}
+ <span
+ :class="{
+ 'bold text-plain': displayMaskedError,
+ }"
+ >
+ {{ __('Requires values to meet regular expression requirements.') }}</span
+ >
<gl-link href="/help/ci/variables/README#masked-variables">{{
__('More information')
}}</gl-link>
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_popover.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_popover.vue
new file mode 100644
index 00000000000..c4b1bc18f5a
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_popover.vue
@@ -0,0 +1,55 @@
+<script>
+import { GlPopover, GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
+
+export default {
+ maxTextLength: 95,
+ components: {
+ GlPopover,
+ GlIcon,
+ GlButton,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ target: {
+ type: String,
+ required: true,
+ },
+ value: {
+ type: String,
+ required: true,
+ },
+ tooltipText: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ displayValue() {
+ if (this.value.length > this.$options.maxTextLength) {
+ return `${this.value.substring(0, this.$options.maxTextLength)}...`;
+ }
+ return this.value;
+ },
+ },
+};
+</script>
+
+<template>
+ <div id="popover-container">
+ <gl-popover :target="target" triggers="hover" placement="top" container="popover-container">
+ <div class="d-flex justify-content-between position-relative">
+ <div class="pr-5 w-100 ci-popover-value">{{ displayValue }}</div>
+ <gl-button
+ v-gl-tooltip
+ class="btn-transparent btn-clipboard position-absolute position-top-0 position-right-0"
+ :title="tooltipText"
+ :data-clipboard-text="value"
+ >
+ <gl-icon name="copy-to-clipboard" />
+ </gl-button>
+ </div>
+ </gl-popover>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
index e240323d2c5..3f2f89ada6f 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue
@@ -3,44 +3,58 @@ import { GlTable, GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { mapState, mapActions } from 'vuex';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
+import CiVariablePopover from './ci_variable_popover.vue';
export default {
modalId: ADD_CI_VARIABLE_MODAL_ID,
+ trueIcon: 'mobile-issue-close',
+ falseIcon: 'close',
+ iconSize: 16,
fields: [
{
key: 'variable_type',
label: s__('CiVariables|Type'),
+ customStyle: { width: '70px' },
},
{
key: 'key',
label: s__('CiVariables|Key'),
+ tdClass: 'text-plain',
+ sortable: true,
+ customStyle: { width: '40%' },
},
{
key: 'value',
label: s__('CiVariables|Value'),
tdClass: 'qa-ci-variable-input-value',
+ customStyle: { width: '40%' },
},
{
key: 'protected',
label: s__('CiVariables|Protected'),
+ customStyle: { width: '100px' },
},
{
key: 'masked',
label: s__('CiVariables|Masked'),
+ customStyle: { width: '100px' },
},
{
key: 'environment_scope',
- label: s__('CiVariables|Environment Scope'),
+ label: s__('CiVariables|Environments'),
+ customStyle: { width: '20%' },
},
{
key: 'actions',
label: '',
+ customStyle: { width: '35px' },
},
],
components: {
GlTable,
GlButton,
GlIcon,
+ CiVariablePopover,
},
directives: {
GlModalDirective,
@@ -64,7 +78,7 @@ export default {
this.fetchVariables();
},
methods: {
- ...mapActions(['fetchVariables', 'deleteVariable', 'toggleValues', 'editVariable']),
+ ...mapActions(['fetchVariables', 'toggleValues', 'editVariable']),
},
};
</script>
@@ -74,42 +88,82 @@ export default {
<gl-table
:fields="fields"
:items="variables"
- responsive
- show-empty
tbody-tr-class="js-ci-variable-row"
+ sort-by="key"
+ sort-direction="asc"
+ stacked="lg"
+ fixed
+ show-empty
+ sort-icon-left
+ no-sort-reset
>
- <template #cell(value)="data">
- <span v-if="valuesHidden">*****************</span>
- <span v-else>{{ data.value }}</span>
+ <template #table-colgroup="scope">
+ <col v-for="field in scope.fields" :key="field.key" :style="field.customStyle" />
+ </template>
+ <template #cell(key)="{ item }">
+ <div class="d-flex truncated-container">
+ <span :id="`ci-variable-key-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
+ item.key
+ }}</span>
+ <ci-variable-popover
+ :target="`ci-variable-key-${item.id}`"
+ :value="item.key"
+ :tooltip-text="__('Copy key')"
+ />
+ </div>
+ </template>
+ <template #cell(value)="{ item }">
+ <span v-if="valuesHidden">*********************</span>
+ <div v-else class="d-flex truncated-container">
+ <span :id="`ci-variable-value-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
+ item.value
+ }}</span>
+ <ci-variable-popover
+ :target="`ci-variable-value-${item.id}`"
+ :value="item.value"
+ :tooltip-text="__('Copy value')"
+ />
+ </div>
+ </template>
+ <template #cell(protected)="{ item }">
+ <gl-icon v-if="item.protected" :size="$options.iconSize" :name="$options.trueIcon" />
+ <gl-icon v-else :size="$options.iconSize" :name="$options.falseIcon" />
+ </template>
+ <template #cell(masked)="{ item }">
+ <gl-icon v-if="item.masked" :size="$options.iconSize" :name="$options.trueIcon" />
+ <gl-icon v-else :size="$options.iconSize" :name="$options.falseIcon" />
</template>
- <template #cell(actions)="data">
+ <template #cell(environment_scope)="{ item }">
+ <div class="d-flex truncated-container">
+ <span :id="`ci-variable-env-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
+ item.environment_scope
+ }}</span>
+ <ci-variable-popover
+ :target="`ci-variable-env-${item.id}`"
+ :value="item.environment_scope"
+ :tooltip-text="__('Copy environment')"
+ />
+ </div>
+ </template>
+ <template #cell(actions)="{ item }">
<gl-button
ref="edit-ci-variable"
v-gl-modal-directive="$options.modalId"
- @click="editVariable(data.item)"
+ @click="editVariable(item)"
>
- <gl-icon name="pencil" />
- </gl-button>
- <gl-button
- ref="delete-ci-variable"
- category="secondary"
- variant="danger"
- @click="deleteVariable(data.item)"
- >
- <gl-icon name="remove" />
+ <gl-icon :size="$options.iconSize" name="pencil" />
</gl-button>
</template>
<template #empty>
- <p ref="empty-variables" class="settings-message text-center empty-variables">
- {{
- __(
- 'There are currently no variables, add a variable with the Add Variable button below.',
- )
- }}
+ <p ref="empty-variables" class="text-center empty-variables text-plain">
+ {{ __('There are no variables yet.') }}
</p>
</template>
</gl-table>
- <div class="ci-variable-actions d-flex justify-content-end">
+ <div
+ class="ci-variable-actions d-flex justify-content-end"
+ :class="{ 'justify-content-center': !tableIsNotEmpty }"
+ >
<gl-button
v-if="tableIsNotEmpty"
ref="secret-value-reveal-button"
diff --git a/app/assets/javascripts/ci_variable_list/constants.js b/app/assets/javascripts/ci_variable_list/constants.js
index bfc9cbbd840..b2fa980c546 100644
--- a/app/assets/javascripts/ci_variable_list/constants.js
+++ b/app/assets/javascripts/ci_variable_list/constants.js
@@ -1,2 +1,16 @@
-// eslint-disable-next-line import/prefer-default-export
+import { __ } from '~/locale';
+
+// eslint-disable import/prefer-default-export
export const ADD_CI_VARIABLE_MODAL_ID = 'add-ci-variable';
+
+export const displayText = {
+ variableText: __('Var'),
+ fileText: __('File'),
+ allEnvironmentsText: __('All'),
+};
+
+export const types = {
+ variableType: 'env_var',
+ fileType: 'file',
+ allEnvironmentsType: '*',
+};
diff --git a/app/assets/javascripts/ci_variable_list/store/mutations.js b/app/assets/javascripts/ci_variable_list/store/mutations.js
index 74e2bcfa2db..c75eb4a91fd 100644
--- a/app/assets/javascripts/ci_variable_list/store/mutations.js
+++ b/app/assets/javascripts/ci_variable_list/store/mutations.js
@@ -1,5 +1,5 @@
import * as types from './mutation_types';
-import { __ } from '~/locale';
+import { displayText } from '../constants';
export default {
[types.REQUEST_VARIABLES](state) {
@@ -61,7 +61,7 @@ export default {
[types.RECEIVE_ENVIRONMENTS_SUCCESS](state, environments) {
state.isLoading = false;
state.environments = environments;
- state.environments.unshift(__('All environments'));
+ state.environments.unshift(displayText.allEnvironmentsText);
},
[types.VARIABLE_BEING_EDITED](state, variable) {
@@ -70,12 +70,12 @@ export default {
[types.CLEAR_MODAL](state) {
state.variable = {
- variable_type: __('Variable'),
+ variable_type: displayText.variableText,
key: '',
secret_value: '',
protected: false,
masked: false,
- environment_scope: __('All environments'),
+ environment_scope: displayText.allEnvironmentsText,
};
},
diff --git a/app/assets/javascripts/ci_variable_list/store/state.js b/app/assets/javascripts/ci_variable_list/store/state.js
index c5e0bbfdbf4..5166321d6a7 100644
--- a/app/assets/javascripts/ci_variable_list/store/state.js
+++ b/app/assets/javascripts/ci_variable_list/store/state.js
@@ -1,4 +1,4 @@
-import { __ } from '~/locale';
+import { displayText } from '../constants';
export default () => ({
endpoint: null,
@@ -8,17 +8,17 @@ export default () => ({
isLoading: false,
isDeleting: false,
variable: {
- variable_type: __('Variable'),
+ variable_type: displayText.variableText,
key: '',
secret_value: '',
protected: false,
masked: false,
- environment_scope: __('All environments'),
+ environment_scope: displayText.allEnvironmentsText,
},
variables: null,
valuesHidden: true,
error: null,
environments: [],
- typeOptions: [__('Variable'), __('File')],
+ typeOptions: [displayText.variableText, displayText.fileText],
variableBeingEdited: null,
});
diff --git a/app/assets/javascripts/ci_variable_list/store/utils.js b/app/assets/javascripts/ci_variable_list/store/utils.js
index 0b9932d9bb5..3cd8c85024b 100644
--- a/app/assets/javascripts/ci_variable_list/store/utils.js
+++ b/app/assets/javascripts/ci_variable_list/store/utils.js
@@ -1,23 +1,22 @@
-import { __ } from '~/locale';
import { cloneDeep } from 'lodash';
+import { displayText, types } from '../constants';
-const variableType = 'env_var';
-const fileType = 'file';
-
-const variableTypeHandler = type => (type === 'Variable' ? variableType : fileType);
+const variableTypeHandler = type =>
+ type === displayText.variableText ? types.variableType : types.fileType;
export const prepareDataForDisplay = variables => {
const variablesToDisplay = [];
variables.forEach(variable => {
const variableCopy = variable;
- if (variableCopy.variable_type === variableType) {
- variableCopy.variable_type = __('Variable');
+ if (variableCopy.variable_type === types.variableType) {
+ variableCopy.variable_type = displayText.variableText;
} else {
- variableCopy.variable_type = __('File');
+ variableCopy.variable_type = displayText.fileText;
}
+ variableCopy.secret_value = variableCopy.value;
- if (variableCopy.environment_scope === '*') {
- variableCopy.environment_scope = __('All environments');
+ if (variableCopy.environment_scope === types.allEnvironmentsType) {
+ variableCopy.environment_scope = displayText.allEnvironmentsText;
}
variablesToDisplay.push(variableCopy);
});
@@ -29,9 +28,8 @@ export const prepareDataForApi = (variable, destroy = false) => {
variableCopy.protected = variableCopy.protected.toString();
variableCopy.masked = variableCopy.masked.toString();
variableCopy.variable_type = variableTypeHandler(variableCopy.variable_type);
-
- if (variableCopy.environment_scope === __('All environments')) {
- variableCopy.environment_scope = __('*');
+ if (variableCopy.environment_scope === displayText.allEnvironmentsText) {
+ variableCopy.environment_scope = types.allEnvironmentsType;
}
if (destroy) {