Commit 14eaeb40 authored by Justin Ho's avatar Justin Ho

Extract integration form custom fields to Vue

- Use a DynamicField component that uses the correct GitLab UI
component depending on the field type.
- Add backend structure to pass form schema and data to Vue
parent 264459e7
<script>
import { startCase, isEmpty } from 'lodash';
import { __, sprintf } from '~/locale';
import { GlFormGroup, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormTextarea } from '@gitlab/ui';
export default {
name: 'DynamicField',
components: {
GlFormGroup,
GlFormCheckbox,
GlFormInput,
GlFormSelect,
GlFormTextarea,
},
props: {
choices: {
type: Array,
required: false,
default: null,
},
help: {
type: String,
required: false,
default: null,
},
name: {
type: String,
required: true,
},
placeholder: {
type: String,
required: false,
default: null,
},
required: {
type: Boolean,
required: false,
},
title: {
type: String,
required: false,
default: null,
},
type: {
type: String,
required: true,
},
value: {
type: String,
required: false,
default: null,
},
},
data() {
return {
model: this.value,
};
},
computed: {
isCheckbox() {
return this.type === 'checkbox';
},
isPassword() {
return this.type === 'password';
},
isSelect() {
return this.type === 'select';
},
isTextarea() {
return this.type === 'textarea';
},
isNonEmptyPassword() {
return this.isPassword && !isEmpty(this.value);
},
label() {
if (this.isNonEmptyPassword) {
return sprintf(__('Enter new %{field_title}'), {
field_title: this.computedTitle,
});
}
return this.computedTitle;
},
passwordRequired() {
return isEmpty(this.value) && this.required;
},
computedTitle() {
return this.title || startCase(this.name);
},
options() {
return this.choices.map(choice => {
return {
value: choice[1],
text: choice[0],
};
});
},
fieldId() {
return `service_${this.name}`;
},
fieldName() {
return `service[${this.name}]`;
},
},
created() {
if (this.isNonEmptyPassword) {
this.model = null;
}
},
};
</script>
<template>
<gl-form-group :label="label" :label-for="fieldId" :description="help">
<template v-if="isCheckbox">
<input :name="fieldName" type="hidden" value="false" />
<gl-form-checkbox :id="fieldId" v-model="model" :name="fieldName" />
</template>
<gl-form-select
v-else-if="isSelect"
:id="fieldId"
v-model="model"
:name="fieldName"
:options="options"
/>
<gl-form-textarea
v-else-if="isTextarea"
:id="fieldId"
v-model="model"
:name="fieldName"
:placeholder="placeholder"
:required="required"
/>
<gl-form-input
v-else-if="isPassword"
:id="fieldId"
v-model="model"
:name="fieldName"
:type="type"
autocomplete="new-password"
:placeholder="placeholder"
:required="passwordRequired"
/>
<gl-form-input
v-else
:id="fieldId"
v-model="model"
:name="fieldName"
:type="type"
:placeholder="placeholder"
:required="required"
/>
</gl-form-group>
</template>
......@@ -2,6 +2,7 @@
import ActiveToggle from './active_toggle.vue';
import JiraTriggerFields from './jira_trigger_fields.vue';
import TriggerFields from './trigger_fields.vue';
import DynamicField from './dynamic_field.vue';
export default {
name: 'IntegrationForm',
......@@ -9,6 +10,7 @@ export default {
ActiveToggle,
JiraTriggerFields,
TriggerFields,
DynamicField,
},
props: {
activeToggleProps: {
......@@ -28,6 +30,11 @@ export default {
required: false,
default: () => [],
},
fields: {
type: Array,
required: false,
default: () => [],
},
type: {
type: String,
required: true,
......@@ -46,5 +53,6 @@ export default {
<active-toggle v-if="showActive" v-bind="activeToggleProps" />
<jira-trigger-fields v-if="isJira" v-bind="triggerFieldsProps" />
<trigger-fields v-else-if="triggerEvents.length" :events="triggerEvents" :type="type" />
<dynamic-field v-for="field in fields" :key="field.name" v-bind="field" />
</div>
</template>
......@@ -15,7 +15,7 @@ export default el => {
return result;
}
const { type, commentDetail, triggerEvents, ...booleanAttributes } = el.dataset;
const { type, commentDetail, triggerEvents, fields, ...booleanAttributes } = el.dataset;
const {
showActive,
activated,
......@@ -41,6 +41,7 @@ export default el => {
initialCommentDetail: commentDetail,
},
triggerEvents: JSON.parse(triggerEvents),
fields: JSON.parse(fields),
},
});
},
......
......@@ -171,6 +171,20 @@ class Service < ApplicationRecord
fields
end
def global_fields_json
global_fields.map do |field|
value = send(field[:name]) # rubocop:disable GitlabSecurity/PublicSend
public_value =
if field[:type] == 'password' && value.present?
'true'
else
value
end
field.merge(value: public_value)
end.to_json
end
def configurable_events
events = self.class.supported_events
......
= form_errors(@service)
- trigger_events = Feature.enabled?(:integration_form_refactor) ? ServiceEventSerializer.new(service: @service).represent(@service.configurable_events).to_json : []
- fields = @service.global_fields_json
- if lookup_context.template_exists?('help', "projects/services/#{@service.to_param}", true)
= render "projects/services/#{@service.to_param}/help", subject: @service
......@@ -10,7 +11,7 @@
.service-settings
.js-vue-integration-settings{ data: { show_active: @service.show_active_box?.to_s, activated: (@service.active || @service.new_record?).to_s, type: @service.to_param, merge_request_events: @service.merge_requests_events.to_s,
commit_events: @service.commit_events.to_s, enable_comments: @service.comment_on_event_enabled.to_s, comment_detail: @service.comment_detail, trigger_events: trigger_events } }
commit_events: @service.commit_events.to_s, enable_comments: @service.comment_on_event_enabled.to_s, comment_detail: @service.comment_detail, trigger_events: trigger_events, fields: fields } }
- if @service.configurable_events.present? && !@service.is_a?(JiraService) && Feature.disabled?(:integration_form_refactor)
.form-group.row
......
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