Skip to content

Commit

Permalink
feat(entities-plugins): support field grouping (#1224)
Browse files Browse the repository at this point in the history
  • Loading branch information
sumimakito authored Mar 14, 2024
1 parent 35c6f91 commit 58ef983
Show file tree
Hide file tree
Showing 9 changed files with 303 additions and 41 deletions.
98 changes: 87 additions & 11 deletions packages/core/forms/src/generator/FormGenerator.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,97 @@
<template lang="pug">
div.vue-form-generator(v-if='schema != null')
fieldset(v-if="schema.fields", :is='tag')
template(v-for='field in fields')
form-group(v-if='fieldVisible(field)', ref="children", :vfg="vfg", :field="field", :errors="errors", :model="model", :options="options", @validated="onFieldValidated", @model-updated="onModelUpdated")

template(v-for='group in groups')
fieldset(:is='tag', :class='getFieldRowClasses(group)')
legend(v-if='group.legend') {{ group.legend }}
template(v-for='field in group.fields')
form-group(v-if='fieldVisible(field)', ref="children", :vfg="vfg", :field="field", :errors="errors", :model="model", :options="options", @validated="onFieldValidated", @model-updated="onModelUpdated")
<template>
<div
v-if="schema != null"
class="vue-form-generator"
>
<component
:is="tag"
v-if="schema.fields"
>
<template
v-for="field in fields"
:key="field.model"
>
<form-group
v-if="fieldVisible(field)"
ref="children"
:errors="errors"
:field="field"
:model="model"
:options="options"
:vfg="vfg"
@model-updated="onModelUpdated"
@validated="onFieldValidated"
/>
</template>
</component>

<template
v-for="(group, i) in groups"
:key="`group-${i}`"
>
<KCollapse
v-if="group.collapsible"
:model-value="group.collapsedByDefault === undefined ? false : group.collapsedByDefault"
:title="group.legend"
>
<component
:is="tag"
:class="getFieldRowClasses(group)"
>
<template
v-for="field in group.fields"
:key="field.model"
>
<form-group
v-if="fieldVisible(field)"
ref="children"
:errors="errors"
:field="field"
:model="model"
:options="options"
:vfg="vfg"
@model-updated="onModelUpdated"
@validated="onFieldValidated"
/>
</template>
</component>
</KCollapse>

<component
:is="tag"
v-else
:class="getFieldRowClasses(group)"
>
<legend v-if="group.legend">
{{ group.legend }}
</legend>
<template
v-for="field in group.fields"
:key="field.model"
>
<form-group
v-if="fieldVisible(field)"
ref="children"
:errors="errors"
:field="field"
:model="model"
:options="options"
:vfg="vfg"
@model-updated="onModelUpdated"
@validated="onFieldValidated"
/>
</template>
</component>
</template>
</div>
</template>

<script>
import { get as objGet, forEach, isFunction, isNil, isArray } from 'lodash'
import formMixin from './FormMixin.vue'
import formGroup from './FormGroup.vue'
import { ref } from 'vue'
export default {
name: 'FormGenerator',
components: { formGroup },
Expand Down
6 changes: 6 additions & 0 deletions packages/entities/entities-plugins/docs/plugin-form.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ A form component for Plugins.
- default: `false`
- *Specific to Kong Manager*. Whether or not to hide the consumer group scope field.

- `groupFields`:
- type: `boolean`
- required: `false`
- default: `false`
- Whether to enable grouping for required and advanced (optional) fields.

The base konnect or kongManger config.

#### `pluginType`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const konnectConfig = ref<KonnectPluginFormConfig>({
backRoute: { name: 'select-plugin' },
cancelRoute: { name: 'home' },
jsonYamlFormsEnabled: true,
groupFields: true,
})
const kongManagerConfig = ref<KongManagerPluginFormConfig>({
Expand All @@ -62,6 +63,7 @@ const kongManagerConfig = ref<KongManagerPluginFormConfig>({
// entityId: '123-abc-i-lover-cats',
backRoute: { name: 'select-plugin' },
cancelRoute: { name: 'home' },
groupFields: true,
})
const onUpdate = (payload: Record<string, any>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,23 @@
</template>

<script lang="ts">
import { computed, ref, reactive, provide, watch, type PropType, onBeforeMount, defineComponent } from 'vue'
import type { AxiosResponse, AxiosRequestConfig } from 'axios'
import {
type PluginEntityInfo,
type KonnectPluginFormConfig,
type KongManagerPluginFormConfig,
} from '../types'
import { useAxios, useHelpers } from '@kong-ui-public/entities-shared'
import {
FORMS_API_KEY,
customFields,
getSharedFormName,
FORMS_API_KEY,
sharedForms,
} from '@kong-ui-public/forms'
import '@kong-ui-public/forms/dist/style.css'
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
import { computed, defineComponent, onBeforeMount, provide, reactive, ref, watch, type PropType } from 'vue'
import composables from '../composables'
import endpoints from '../plugins-endpoints'
import {
type KongManagerPluginFormConfig,
type KonnectPluginFormConfig,
type PluginEntityInfo,
} from '../types'
// Must explicitly specify these as components since they are rendered dynamically
export default defineComponent({
Expand Down Expand Up @@ -120,7 +120,9 @@ const { axiosInstance } = useAxios({
headers: props.config.requestHeaders,
})
const { parseSchema } = composables.useSchemas(props.entityMap.focusedEntity?.id || undefined)
const { parseSchema } = composables.useSchemas(props.entityMap.focusedEntity?.id || undefined, {
groupFields: props.config.groupFields,
})
const { convertToDotNotation, unFlattenObject, isObjectEmpty, unsetNullForeignKey } = composables.usePluginHelpers()
const { objectsAreEqual } = useHelpers()
Expand Down Expand Up @@ -616,7 +618,12 @@ onBeforeMount(() => {
}
:deep(.vue-form-generator) {
& > fieldset {
.k-collapse:not(:last-child) {
border-bottom: $kui-border-width-10 solid $kui-color-border;
margin-bottom: $kui-space-80;
}
fieldset {
border: none;
padding: $kui-space-0;
}
Expand Down
76 changes: 76 additions & 0 deletions packages/entities/entities-plugins/src/components/PluginForm.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,82 @@ describe('<PluginForm />', () => {
cy.get('#config-private_network').should('be.visible')
})

it.only('should show common, required, and advanced fields when groupFields is true', () => {
interceptKMSchema({ mockData: schema2 })

cy.mount(PluginForm, {
global: { components: { VueFormGenerator } },
props: {
config: {
...baseConfigKM,
groupFields: true,
},
pluginType: 'mocking',
},
router,
})

cy.wait('@getPluginSchema')
cy.get('.kong-ui-entities-plugin-form-container').should('be.visible')

// button state
cy.getTestId('form-submit').should('be.visible')
cy.getTestId('form-submit').should('be.enabled')
cy.getTestId('form-back').should('be.visible')
cy.getTestId('form-back').should('be.enabled')
cy.getTestId('form-cancel').should('be.visible')

// scope fields
cy.get('.field-selectionGroup').should('be.visible')
cy.get('.Global-check').should('be.visible')
cy.get('.Scoped-check').should('be.visible')
cy.get('.field-selectionGroup').find('.field-AutoSuggest').should('not.be.visible')
cy.get('.Scoped-check input').click()
cy.get('.field-selectionGroup').find('.field-AutoSuggest').should('be.visible')
cy.get('#service-id').should('be.visible')
cy.get('#route-id').should('be.visible')

cy.getTestId('k-collapse-title')
.contains('Common Fields')
.parents('.k-collapse')
.first()
.as('commonFields')

cy.getTestId('k-collapse-title')
.contains('Required Fields')
.parents('.k-collapse')
.first()
.as('requiredFields')

cy.getTestId('k-collapse-title')
.contains('Advanced Fields')
.parents('.k-collapse')
.first()
.as('advancedFields')

// common fields
cy.get('@commonFields').find('#enabled').should('exist')
cy.get('@commonFields').find('#instance_name').should('exist')
cy.get('@commonFields').find('#tags').should('exist')
cy.get('@commonFields').find('.plugin-protocols-select').should('be.visible')

// required fields
cy.get('@requiredFields').find('#config-include_base_path').should('be.visible')
cy.get('@requiredFields').find('#config-random_status_code').should('be.visible')

// advanced fields should be hidden by default
cy.get('@advancedFields').findTestId('k-collapse-hidden-content').should('be.hidden')

// reveal them
cy.get('@advancedFields').findTestId('k-collapse-trigger-content').click()
cy.get('@advancedFields').findTestId('k-collapse-hidden-content').should('be.visible')

// advanced fields
cy.get('@advancedFields').find('#config-api_specification').should('be.visible')
cy.get('@advancedFields').find('#config-api_specification_filename').should('be.visible')
cy.get('@advancedFields').find('#config-included_status_codes').should('be.visible')
})

it('should hide scope selection when hideScopeSelection is true', () => {
interceptKMSchema()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ const props = defineProps({
const router = useRouter()
const { i18n: { t } } = composables.useI18n()
const { pluginMetaData, credentialMetaData, credentialSchemas } = composables.usePluginMetaData()
const { customSchemas, typedefs } = composables.useSchemas(undefined, props.config.app)
const { customSchemas, typedefs } = composables.useSchemas(undefined, { app: props.config.app })
const { getMessageFromError } = useErrors()
const { capitalize } = useStringHelpers()
const { objectsAreEqual } = useHelpers()
Expand Down Expand Up @@ -512,6 +512,7 @@ const buildFormSchema = (parentKey: string, response: Record<string, any>, initi
initialFormSchema[field] = { id: field } // each field's key will be set as the id
initialFormSchema[field].type = scheme.type === 'boolean' ? 'checkbox' : 'input'
initialFormSchema[field].required = scheme.required
if (field.startsWith('config-')) {
initialFormSchema[field].label = formatPluginFieldLabel(field)
Expand Down
Loading

0 comments on commit 58ef983

Please sign in to comment.