From e142939929305dc0b7605aea692fac2f70a59e80 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Mon, 8 Jan 2024 12:11:31 +0700 Subject: [PATCH] fix(ui): apply controlled input to field [EE-6411] (#10738) --- app/edge/react/components/index.ts | 6 ++-- .../react/components/applications.ts | 6 ++++ app/kubernetes/react/components/index.ts | 34 ++++++++----------- .../environments-list-view-components.ts | 13 ------- app/portainer/react/components/index.ts | 10 ++---- .../EnvironmentVariablesPanel.tsx | 12 +++---- .../gitops/AuthFieldset/AuthFieldset.tsx | 5 ++- app/react/portainer/gitops/GitForm.tsx | 5 ++- 8 files changed, 41 insertions(+), 50 deletions(-) create mode 100644 app/kubernetes/react/components/applications.ts delete mode 100644 app/portainer/react/components/environments-list-view-components.ts diff --git a/app/edge/react/components/index.ts b/app/edge/react/components/index.ts index 54fba8757..0835e50c3 100644 --- a/app/edge/react/components/index.ts +++ b/app/edge/react/components/index.ts @@ -15,7 +15,7 @@ import { AssociatedEdgeEnvironmentsSelector } from '@/react/edge/components/Asso import { EnvironmentsDatatable } from '@/react/edge/edge-stacks/ItemView/EnvironmentsDatatable'; import { TemplateFieldset } from '@/react/edge/edge-stacks/CreateView/TemplateFieldset'; -export const componentsModule = angular +const ngModule = angular .module('portainer.edge.react.components', []) .component( 'edgeStackEnvironmentsDatatable', @@ -104,4 +104,6 @@ export const componentsModule = angular .component( 'edgeStackCreateTemplateFieldset', r2a(withReactQuery(TemplateFieldset), ['setValues', 'values', 'errors']) - ).name; + ); + +export const componentsModule = ngModule.name; diff --git a/app/kubernetes/react/components/applications.ts b/app/kubernetes/react/components/applications.ts new file mode 100644 index 000000000..eb5dd6db2 --- /dev/null +++ b/app/kubernetes/react/components/applications.ts @@ -0,0 +1,6 @@ +import angular from 'angular'; + +export const applicationsModule = angular.module( + 'portainer.kubernetes.react.components.applications', + [] +).name; diff --git a/app/kubernetes/react/components/index.ts b/app/kubernetes/react/components/index.ts index e5ef79c44..42a5ad9e7 100644 --- a/app/kubernetes/react/components/index.ts +++ b/app/kubernetes/react/components/index.ts @@ -3,7 +3,6 @@ import angular from 'angular'; import { r2a } from '@/react-tools/react2angular'; import { IngressClassDatatableAngular } from '@/react/kubernetes/cluster/ingressClass/IngressClassDatatable/IngressClassDatatableAngular'; import { NamespacesSelector } from '@/react/kubernetes/cluster/RegistryAccessView/NamespacesSelector'; -import { StorageAccessModeSelector } from '@/react/kubernetes/cluster/ConfigureView/ConfigureForm/StorageAccessModeSelector'; import { NamespaceAccessUsersSelector } from '@/react/kubernetes/namespaces/AccessView/NamespaceAccessUsersSelector'; import { RegistriesSelector } from '@/react/kubernetes/namespaces/components/RegistriesFormSection/RegistriesSelector'; import { KubeServicesForm } from '@/react/kubernetes/applications/CreateView/application-services/KubeServicesForm'; @@ -51,8 +50,10 @@ import { withControlledInput } from '@/react-tools/withControlledInput'; import { EnvironmentVariablesFieldset } from '@@/form-components/EnvironmentVariablesFieldset'; +import { applicationsModule } from './applications'; + export const ngModule = angular - .module('portainer.kubernetes.react.components', []) + .module('portainer.kubernetes.react.components', [applicationsModule]) .component( 'ingressClassDatatable', r2a(IngressClassDatatableAngular, [ @@ -78,16 +79,6 @@ export const ngModule = angular 'value', ]) ) - .component( - 'storageAccessModeSelector', - r2a(StorageAccessModeSelector, [ - 'inputId', - 'onChange', - 'options', - 'value', - 'storageClassName', - ]) - ) .component( 'namespaceAccessUsersSelector', r2a(NamespaceAccessUsersSelector, [ @@ -140,11 +131,12 @@ export const ngModule = angular ) .component( 'kubeStackName', - r2a(withUIRouter(withReactQuery(withCurrentUser(StackName))), [ - 'setStackName', - 'isAdmin', - 'stackName', - ]) + r2a( + withUIRouter( + withReactQuery(withCurrentUser(withControlledInput(StackName))) + ), + ['setStackName', 'isAdmin', 'stackName'] + ) ) .component( 'applicationSummaryWidget', @@ -202,8 +194,12 @@ export const componentsModule = ngModule.name; withFormValidation( ngModule, - withControlledInput( - withUIRouter(withCurrentUser(withReactQuery(KubeServicesForm))) + withUIRouter( + withCurrentUser( + withReactQuery( + withControlledInput(KubeServicesForm, { values: 'onChange' }) + ) + ) ), 'kubeServicesForm', ['values', 'onChange', 'appName', 'selector', 'isEditMode', 'namespace'], diff --git a/app/portainer/react/components/environments-list-view-components.ts b/app/portainer/react/components/environments-list-view-components.ts deleted file mode 100644 index da3343637..000000000 --- a/app/portainer/react/components/environments-list-view-components.ts +++ /dev/null @@ -1,13 +0,0 @@ -import angular from 'angular'; - -import { r2a } from '@/react-tools/react2angular'; -import { ImportFdoDeviceButton } from '@/react/portainer/environments/ListView/ImportFdoDeviceButton'; -import { withUIRouter } from '@/react-tools/withUIRouter'; -import { withReactQuery } from '@/react-tools/withReactQuery'; - -export const envListModule = angular - .module('portainer.app.react.components.environments.list-view', []) - .component( - 'importFdoDeviceButton', - r2a(withUIRouter(withReactQuery(ImportFdoDeviceButton)), []) - ).name; diff --git a/app/portainer/react/components/index.ts b/app/portainer/react/components/index.ts index 5e1eb2920..09ed446bb 100644 --- a/app/portainer/react/components/index.ts +++ b/app/portainer/react/components/index.ts @@ -9,6 +9,7 @@ import { withFormValidation } from '@/react-tools/withFormValidation'; import { GroupAssociationTable } from '@/react/portainer/environments/environment-groups/components/GroupAssociationTable'; import { AssociatedEnvironmentsSelector } from '@/react/portainer/environments/environment-groups/components/AssociatedEnvironmentsSelector'; import { HelmRepositoryDatatable } from '@/react/portainer/account/AccountView/HelmRepositoryDatatable'; +import { withControlledInput } from '@/react-tools/withControlledInput'; import { EnvironmentVariablesFieldset, @@ -21,7 +22,6 @@ import { PageHeader } from '@@/PageHeader'; import { TagSelector } from '@@/TagSelector'; import { Loading } from '@@/Widget/Loading'; import { PasswordCheckHint } from '@@/PasswordCheckHint'; -import { ViewLoading } from '@@/ViewLoading'; import { Tooltip } from '@@/Tip/Tooltip'; import { Badge } from '@@/Badge'; import { TableColumnHeaderAngular } from '@@/datatables/TableHeaderCell'; @@ -43,7 +43,6 @@ import { gitFormModule } from './git-form'; import { settingsModule } from './settings'; import { accessControlModule } from './access-control'; import { environmentsModule } from './environments'; -import { envListModule } from './environments-list-view-components'; import { registriesModule } from './registries'; import { accountModule } from './account'; @@ -51,7 +50,6 @@ export const ngModule = angular .module('portainer.app.react.components', [ accessControlModule, customTemplatesModule, - envListModule, environmentsModule, gitFormModule, registriesModule, @@ -106,7 +104,6 @@ export const ngModule = angular 'isSortedDesc', ]) ) - .component('viewLoading', r2a(ViewLoading, ['message'])) .component( 'pageHeader', r2a(withUIRouter(withReactQuery(withCurrentUser(PageHeader))), [ @@ -198,7 +195,6 @@ export const ngModule = angular 'dataCy', ]) ) - .component( 'reactCodeEditor', r2a(CodeEditor, [ @@ -240,7 +236,7 @@ export const componentsModule = ngModule.name; withFormValidation( ngModule, - EnvironmentVariablesFieldset, + withControlledInput(EnvironmentVariablesFieldset, { values: 'onChange' }), 'environmentVariablesFieldset', ['canUndoDelete'], envVarValidation @@ -248,7 +244,7 @@ withFormValidation( withFormValidation( ngModule, - EnvironmentVariablesPanel, + withControlledInput(EnvironmentVariablesPanel, { values: 'onChange' }), 'environmentVariablesPanel', ['explanation', 'showHelpMessage', 'isFoldable'], envVarValidation diff --git a/app/react/components/form-components/EnvironmentVariablesFieldset/EnvironmentVariablesPanel.tsx b/app/react/components/form-components/EnvironmentVariablesFieldset/EnvironmentVariablesPanel.tsx index 668bd6662..5f090f33f 100644 --- a/app/react/components/form-components/EnvironmentVariablesFieldset/EnvironmentVariablesPanel.tsx +++ b/app/react/components/form-components/EnvironmentVariablesFieldset/EnvironmentVariablesPanel.tsx @@ -1,11 +1,12 @@ +import { ComponentProps } from 'react'; + import { FormSection } from '@@/form-components/FormSection'; import { TextTip } from '@@/Tip/TextTip'; -import { ArrayError } from '../InputList/InputList'; - -import { Values } from './types'; import { EnvironmentVariablesFieldset } from './EnvironmentVariablesFieldset'; +type FieldsetProps = ComponentProps; + export function EnvironmentVariablesPanel({ explanation, onChange, @@ -15,12 +16,9 @@ export function EnvironmentVariablesPanel({ isFoldable = false, }: { explanation?: string; - values: Values; - onChange(value: Values): void; showHelpMessage?: boolean; - errors?: ArrayError; isFoldable?: boolean; -}) { +} & FieldsetProps) { return ( handleChange({ RepositoryUsername: username }) @@ -139,6 +141,7 @@ export function AuthFieldset({ function handleChange(partialValue: Partial) { onChange(partialValue); + setValue((value) => ({ ...value, ...partialValue })); } } diff --git a/app/react/portainer/gitops/GitForm.tsx b/app/react/portainer/gitops/GitForm.tsx index 5a34d65cd..7fe6c2f8f 100644 --- a/app/react/portainer/gitops/GitForm.tsx +++ b/app/react/portainer/gitops/GitForm.tsx @@ -1,5 +1,6 @@ import { array, boolean, object, SchemaOf, string } from 'yup'; import { FormikErrors } from 'formik'; +import { useState } from 'react'; import { ComposePathField } from '@/react/portainer/gitops/ComposePathField'; import { RefField } from '@/react/portainer/gitops/RefField'; @@ -35,7 +36,7 @@ interface Props { } export function GitForm({ - value, + value: initialValue, onChange, environmentType = 'DOCKER', deployMethod = 'compose', @@ -48,6 +49,7 @@ export function GitForm({ webhookId, webhooksDocs, }: Props) { + const [value, setValue] = useState(initialValue); // TODO: remove this state when form is not inside angularjs return ( ) { onChange(partialValue); + setValue((value) => ({ ...value, ...partialValue })); } }