From c8ba6321c2936e6f233e7dd8c2d8a6af1980b715 Mon Sep 17 00:00:00 2001 From: Vardan Bansal Date: Wed, 13 Sep 2023 22:50:06 -0700 Subject: [PATCH] refactor as per new plugin interfaces --- .../PluginsPanel/PluginsPanel.module.scss | 9 + .../PluginsPanel.module.scss.d.ts | 2 + .../components/PluginsPanel/PluginsPanel.tsx | 164 +++++++++++------- .../AddUpdatePipeline/AddUpdatePipeline.tsx | 68 +++++--- 4 files changed, 151 insertions(+), 92 deletions(-) diff --git a/web/src/components/PluginsPanel/PluginsPanel.module.scss b/web/src/components/PluginsPanel/PluginsPanel.module.scss index b4666d2c3..b52a9eeaf 100644 --- a/web/src/components/PluginsPanel/PluginsPanel.module.scss +++ b/web/src/components/PluginsPanel/PluginsPanel.module.scss @@ -43,3 +43,12 @@ text-overflow: ellipsis; width: calc(30vw - 2 * var(--spacing-huge)); } + +.formFields { + height: calc(100% - var(--spacing-xlarge)); + overflow-y: scroll; +} + +.panelContent { + height: calc(100% - var(--spacing-large)); +} diff --git a/web/src/components/PluginsPanel/PluginsPanel.module.scss.d.ts b/web/src/components/PluginsPanel/PluginsPanel.module.scss.d.ts index d4a531020..544c2ca69 100644 --- a/web/src/components/PluginsPanel/PluginsPanel.module.scss.d.ts +++ b/web/src/components/PluginsPanel/PluginsPanel.module.scss.d.ts @@ -2,6 +2,8 @@ // This is an auto-generated file export declare const arrow: string export declare const form: string +export declare const formFields: string +export declare const panelContent: string export declare const plugin: string export declare const pluginDesc: string export declare const pluginDetailsPanel: string diff --git a/web/src/components/PluginsPanel/PluginsPanel.tsx b/web/src/components/PluginsPanel/PluginsPanel.tsx index 01ca85754..d72d096b0 100644 --- a/web/src/components/PluginsPanel/PluginsPanel.tsx +++ b/web/src/components/PluginsPanel/PluginsPanel.tsx @@ -1,12 +1,11 @@ import React, { useCallback, useEffect, useState } from 'react' import { Formik } from 'formik' import { capitalize, get } from 'lodash-es' +import { Classes, PopoverInteractionKind, PopoverPosition } from '@blueprintjs/core' import { Color, FontVariation } from '@harnessio/design-system' import { Icon, type IconName } from '@harnessio/icons' -import { Button, ButtonVariation, Container, FormInput, FormikForm, Layout, Text } from '@harnessio/uicore' +import { Button, ButtonVariation, Container, FormInput, FormikForm, Layout, Popover, Text } from '@harnessio/uicore' import { useStrings } from 'framework/strings' -import type { TypesPlugin } from 'services/code' -import { YamlVersion } from 'pages/AddUpdatePipeline/Constants' import pluginList from './plugins/plugins.json' @@ -23,14 +22,30 @@ enum PluginPanelView { Configuration } -interface PluginInterface { +interface PluginInput { + type: 'string' + description?: string + default?: string + options?: { isExtended?: boolean } +} + +interface Plugin { + name: string + spec: { + name: string + description?: string + inputs: { [key: string]: PluginInput } + } +} + +interface PluginCategoryInterface { category: PluginCategory name: string description: string icon: IconName } -const PluginCategories: PluginInterface[] = [ +const PluginCategories: PluginCategoryInterface[] = [ { category: PluginCategory.Harness, name: 'Run', @@ -40,50 +55,34 @@ const PluginCategories: PluginInterface[] = [ { category: PluginCategory.Drone, name: 'Drone', description: 'Run Drone plugins', icon: 'ci-infra' } ] -const dronePluginSpecMockData = { - inputs: { - channel: { - type: 'string' - }, - token: { - type: 'string' - } - }, - steps: [ - { - type: 'script', - spec: { - image: 'plugins/slack' - }, - envs: { - PLUGIN_CHANNEL: '<+inputs.channel>' +const RunStep: Plugin = { + name: 'run', + spec: { + name: 'Run', + inputs: { + script: { + type: 'string', + options: { isExtended: true } } } - ] -} - -const runStepSpec = { - inputs: { - script: { - type: 'string' - } } } export interface PluginsPanelInterface { - version?: YamlVersion onPluginAddUpdate: (isUpdate: boolean, pluginFormData: Record) => void } -export const PluginsPanel = ({ version = YamlVersion.V0, onPluginAddUpdate }: PluginsPanelInterface): JSX.Element => { +export const PluginsPanel = ({ onPluginAddUpdate }: PluginsPanelInterface): JSX.Element => { const { getString } = useStrings() const [category, setCategory] = useState() const [panelView, setPanelView] = useState(PluginPanelView.Category) - const [plugin, setPlugin] = useState() - const [plugins, setPlugins] = useState[]>() + const [plugin, setPlugin] = useState() + const [plugins, setPlugins] = useState() const [loading] = useState(false) const fetchPlugins = () => { + /* temporarily done till api response gets available */ + //@ts-ignore setPlugins(pluginList) } @@ -96,7 +95,7 @@ export const PluginsPanel = ({ version = YamlVersion.V0, onPluginAddUpdate }: Pl const renderPluginCategories = (): JSX.Element => { return ( <> - {PluginCategories.map((item: PluginInterface) => { + {PluginCategories.map((item: PluginCategoryInterface) => { const { name, category: pluginCategory, description, icon } = item return ( - {plugins?.map((_plugin: Record) => { - const { name: uid, description } = _plugin.spec + {plugins?.map((pluginItem: Plugin) => { + const { name: uid, description } = pluginItem.spec return ( { setPanelView(PluginPanelView.Configuration) - setPlugin(_plugin) + setPlugin(pluginItem) }} key={uid}> @@ -179,40 +178,74 @@ export const PluginsPanel = ({ version = YamlVersion.V0, onPluginAddUpdate }: Pl ) }, [loading, plugins]) - const renderPluginFormField = ({ name, type }: { name: string; type: 'string' }): JSX.Element => { + const generateFriendlyName = useCallback((pluginName: string): string => { + return capitalize(pluginName.split('_').join(' ')) + }, []) + + const generateLabelForPluginField = useCallback( + ({ name, properties }: { name: string; properties: PluginInput }): JSX.Element | string => { + const { description } = properties + return ( + + {name && {generateFriendlyName(name)}} + {description && ( + + + {description} + + + }> + + + )} + + ) + }, + [] + ) + + const renderPluginFormField = ({ name, properties }: { name: string; properties: PluginInput }): JSX.Element => { + const { type, default: defaultValue, options } = properties + const { isExtended } = options || {} + const WrapperComponent = isExtended ? FormInput.TextArea : FormInput.Text return type === 'string' ? ( - {capitalize(name)}} + label={generateLabelForPluginField({ name, properties })} style={{ width: '100%' }} key={name} + placeholder={defaultValue} /> ) : ( <> ) } - const constructPayloadForYAMLInsertion = (isUpdate: boolean, pluginFormData: Record) => { + const constructPayloadForYAMLInsertion = (pluginFormData: Record): Record => { let constructedPayload = { ...pluginFormData } switch (category) { case PluginCategory.Drone: + return { type: 'script', spec: constructedPayload } case PluginCategory.Harness: - constructedPayload = - version === YamlVersion.V1 - ? { type: 'script', spec: { run: get(constructedPayload, 'script', '') } } - : { name: 'run step', commands: [get(constructedPayload, 'script', '')] } + return { type: 'script', spec: { run: get(constructedPayload, 'script', '') } } + default: + return {} } - onPluginAddUpdate?.(isUpdate, constructedPayload) } const renderPluginConfigForm = useCallback((): JSX.Element => { - // TODO obtain plugin input spec by parsing YAML - const inputs = get(category === PluginCategory.Drone ? dronePluginSpecMockData : runStepSpec, 'inputs', {}) + const inputs: { [key: string]: PluginInput } = get(plugin, 'spec.inputs', {}) return ( + spacing="large" + padding={{ left: 'xxlarge', top: 'large', right: 'xxlarge', bottom: 'xxlarge' }} + className={css.panelContent}> - {plugin?.uid ? ( + {plugin?.spec?.name && ( - {getString('addLabel')} {plugin.uid} {getString('plugins.stepLabel')} + {getString('addLabel')} {plugin.spec.name} {getString('plugins.stepLabel')} - ) : ( - <> )} { - constructPayloadForYAMLInsertion(false, formData) + onPluginAddUpdate?.(false, constructPayloadForYAMLInsertion(formData)) }}> - + - - {Object.keys(inputs).map((field: string) => { - const fieldType = get(inputs, `${field}.type`, '') as 'string' - return renderPluginFormField({ name: field, type: fieldType }) - })} - + {inputs && ( + + {Object.keys(inputs).map((field: string) => { + return renderPluginFormField({ name: field, properties: get(inputs, field) }) + })} + + )}