Skip to content

Commit

Permalink
Merge pull request #847 from sgratch/enhance-ocp-provider-help-text-f…
Browse files Browse the repository at this point in the history
…ields

🐾 Enhance OCP provider help text fields
  • Loading branch information
yaacov authored Jan 24, 2024
2 parents f5c1ed8 + 7605f49 commit 4c66a1b
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"{{selectedLength}} hosts selected.": "{{selectedLength}} hosts selected.",
"{{vmCount}} VMs selected ": "{{vmCount}} VMs selected ",
"{{vmDone}} of {{vmCount}} VMs migrated": "{{vmDone}} of {{vmCount}} VMs migrated",
"<br>A service account token with cluster admin privileges, required for authenticating the connection to the API server.": "<br>A service account token with cluster admin privileges, required for authenticating the connection to the API server.",
"<br>A service account token with cluster admin privileges, required for authenticating the connection to the API server.<br>If both <strong>URL</strong> and <strong>Service account bearer token</strong> are left blank, the local OpenShift cluster is used.": "<br>A service account token with cluster admin privileges, required for authenticating the connection to the API server.<br>If both <strong>URL</strong> and <strong>Service account bearer token</strong> are left blank, the local OpenShift cluster is used.",
"<br>Error: The format of the provided token is invalid. Ensure the token is a valid kubernetes service account token.": "<br>Error: The format of the provided token is invalid. Ensure the token is a valid kubernetes service account token.",
"24 hours": "24 hours",
"404: Not Found": "404: Not Found",
"7 days": "7 days",
Expand Down Expand Up @@ -127,13 +130,12 @@
"Error: The format of the provided CA Certificate is invalid. Ensure the CA certificate format is in a PEM encoded X.509 format.": "Error: The format of the provided CA Certificate is invalid. Ensure the CA certificate format is in a PEM encoded X.509 format.",
"Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://rhv-host-example.com/ovirt-engine/api.": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://rhv-host-example.com/ovirt-engine/api.",
"Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk.": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and a path. For example: https://vCenter-host-example.com/sdk.",
"Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and optionally a port. For example: <1>https://api.&#8249;your-openshift-domain&#8250;:6443</1> .": "Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and optionally a port. For example: <1>https://api.&#8249;your-openshift-domain&#8250;:6443</1> .",
"Error: The format of the provided user name is invalid. Ensure the user name doesn't include whitespace characters.": "Error: The format of the provided user name is invalid. Ensure the user name doesn't include whitespace characters.",
"Error: The format of the provided user password is invalid. Ensure the user password doesn't include whitespace characters.": "Error: The format of the provided user password is invalid. Ensure the user password doesn't include whitespace characters.",
"Error: This field must be a boolean.": "Error: This field must be a boolean.",
"Error: this field must be set to a boolean value.": "Error: this field must be set to a boolean value.",
"Error: token is a required field, the token must be a valid kubernetes token.": "Error: token is a required field, the token must be a valid kubernetes token.",
"Error: URL is required and must be valid.": "Error: URL is required and must be valid.",
"Error: URL must be valid.": "Error: URL must be valid.",
"Error: Username is required and must be valid.": "Error: Username is required and must be valid.",
"Error: VDDK init image must be valid.": "Error: VDDK init image must be valid.",
"Failed": "Failed",
Expand Down Expand Up @@ -266,7 +268,6 @@
"Number of virtual machines in OVA files": "Number of virtual machines in OVA files",
"Off": "Off",
"On": "On",
"OpenShift cluster API endpoint. Empty may be used for the host provider.": "OpenShift cluster API endpoint. Empty may be used for the host provider.",
"OpenStack domain for application credential credentials.": "OpenStack domain for application credential credentials.",
"OpenStack domain for password credentials.": "OpenStack domain for password credentials.",
"OpenStack domain name for token credentials.": "OpenStack domain name for token credentials.",
Expand Down Expand Up @@ -302,7 +303,6 @@
"Please enter the maximum age in hours for must gather cleanup, if empty default value will be used.": "Please enter the maximum age in hours for must gather cleanup, if empty default value will be used.",
"Please enter the maximum number of concurrent VM migrations, if empty default value will be used.": "Please enter the maximum number of concurrent VM migrations, if empty default value will be used.",
"Please enter URL for OpenStack services REST APIs.": "Please enter URL for OpenStack services REST APIs.",
"Please enter URL for the kubernetes API server, if empty URL default to this cluster.": "Please enter URL for the kubernetes API server, if empty URL default to this cluster.",
"Pod network": "Pod network",
"Power state": "Power state",
"Powered off": "Powered off",
Expand Down Expand Up @@ -356,7 +356,6 @@
"Source provider": "Source provider",
"Specifies the duration for retaining 'must gather' reports before they are automatically deleted. The default value is -1, which implies automatic cleanup is disabled.": "Specifies the duration for retaining 'must gather' reports before they are automatically deleted. The default value is -1, which implies automatic cleanup is disabled.",
"Specify NFS mount end point serving the OVA file[s].": "Specify NFS mount end point serving the OVA file[s].",
"Specify OpenShift cluster API endpoint, for example, https://<kubernetes API Endpoint>:6443 for OpenShift. Empty may be used for the host provider.": "Specify OpenShift cluster API endpoint, for example, https://<kubernetes API Endpoint>:6443 for OpenShift. Empty may be used for the host provider.",
"Specify OpenStack Identity (Keystone) endpoint, for example, http://controller:5000/v3.": "Specify OpenStack Identity (Keystone) endpoint, for example, http://controller:5000/v3.",
"Specify the type of source provider. Allowed values are ova, ovirt, vsphere, openshift, and openstack. This label is needed to verify the credentials are correct when the remote system is accessible and, for RHV, to retrieve the Manager CA certificate when a third-party certificate is specified.": "Specify the type of source provider. Allowed values are ova, ovirt, vsphere, openshift, and openstack. This label is needed to verify the credentials are correct when the remote system is accessible and, for RHV, to retrieve the Manager CA certificate when a third-party certificate is specified.",
"Specify the VDDK image that you created.": "Specify the VDDK image that you created.",
Expand Down Expand Up @@ -421,15 +420,15 @@
"URL": "URL",
"URL must start with https:// or http:// and contain valid hostname and path": "URL must start with https:// or http:// and contain valid hostname and path",
"URL of the API endpoint of the Red Hat Virtualization Manager (RHVM) on which the source VM is mounted. Ensure that the URL includes the path leading to the RHVM API server, usually /ovirt-engine/api. For example, https://rhv-host-example.com/ovirt-engine/api.": "URL of the API endpoint of the Red Hat Virtualization Manager (RHVM) on which the source VM is mounted. Ensure that the URL includes the path leading to the RHVM API server, usually /ovirt-engine/api. For example, https://rhv-host-example.com/ovirt-engine/api.",
"URL of the Openshift Virtualization API endpoint.": "URL of the Openshift Virtualization API endpoint.",
"URL of the Openshift Virtualization API endpoint.<br>If both <strong>URL</strong> and <strong>Service account bearer token</strong> are left blank, the local OpenShift cluster is used.": "URL of the Openshift Virtualization API endpoint.<br>If both <strong>URL</strong> and <strong>Service account bearer token</strong> are left blank, the local OpenShift cluster is used.",
"URL of the provider": "URL of the provider",
"URL of the provider, leave empty to use this providers URL": "URL of the provider, leave empty to use this providers URL",
"URL of the Red Hat Virtualization Manager (RHVM) API endpoint.": "URL of the Red Hat Virtualization Manager (RHVM) API endpoint.",
"URL of the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the URL includes the \"/ovirt-engine/api\" path. For example: https://rhv-host-example.com/ovirt-engine/api.": "URL of the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the URL includes the \"/ovirt-engine/api\" path. For example: https://rhv-host-example.com/ovirt-engine/api.",
"URL of the SDK endpoint of the vCenter on which the source VM is mounted. Ensure that the URL includes the sdk path, usually /sdk. For example, https://vCenter-host-example.com/sdk. If a certificate for FQDN is specified, the value of this field needs to match the FQDN in the certificate.": "URL of the SDK endpoint of the vCenter on which the source VM is mounted. Ensure that the URL includes the sdk path, usually /sdk. For example, https://vCenter-host-example.com/sdk. If a certificate for FQDN is specified, the value of this field needs to match the FQDN in the certificate.",
"URL of the vCenter SDK endpoint.": "URL of the vCenter SDK endpoint.",
"URL of the vCenter SDK endpoint. Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk.": "URL of the vCenter SDK endpoint. Ensure the URL includes the \"/sdk\" path. For example: https://vCenter-host-example.com/sdk.",
"User ID": "User ID",
"User or service account bearer token for service accounts or user authentication.": "User or service account bearer token for service accounts or user authentication.",
"Username": "Username",
"Validation Failed": "Validation Failed",
"VDDK container image. It is strongly recommended to specify a VDDK image to accelerate migrations.": "VDDK container image. It is strongly recommended to specify a VDDK image to accelerate migrations.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,17 @@
margin: 0;
padding-bottom: var(--pf-global--spacer--md);
}

.forklift-edit-modal-field-error-validation {
color: var(--pf-v5-global--danger-color--100);
font-size: small;
}

.forklift-edit-modal-field-success-validation {
color: var(--pf-v5-global--success-color--100);
font-size: small;
}

.forklift-edit-modal-field-default-validation {
font-size: small;
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const EditModal: React.FC<EditModalProps> = ({
const [alertMessage, setAlertMessage] = useState<ReactNode>(null);
const [value, setValue] = useState(getValueByJsonPath(resource, jsonPath) as string);
const [validation, setValidation] = useState<{
helperText: string;
helperText: string | React.JSX.Element;
validated: ValidationResults;
}>({ helperText: '', validated: undefined });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface EditModalProps {
InputComponent?: ModalInputComponentType;

/** Optional. Helper text that provides additional hints to the user, printed in grayed text under the input field. */
helperText?: string;
helperText?: string | React.JSX.Element;

/** Optional. The URL to which the user will be redirected after the confirmation action. */
redirectTo?: string;
Expand Down Expand Up @@ -75,7 +75,7 @@ export type ModalInputComponentType = React.FC<{
* and 'validated' which indicates the status of the validation and is of type ValidationResults.
*/
export type ValidationHookType = (value: string | number) => {
validationHelpText: string;
validationHelpText: string | React.JSX.Element;
validated: ValidationResults;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import { Trans } from 'react-i18next';
import { useForkliftTranslation } from 'src/utils/i18n';

import { ExternalLink } from '@kubev2v/common';
import { ProviderModel } from '@kubev2v/types';
import { ModalVariant } from '@patternfly/react-core';

Expand All @@ -13,20 +15,52 @@ import { EditProviderURLModalProps } from './EditProviderURLModal';
export const OpenshiftEditURLModal: React.FC<EditProviderURLModalProps> = (props) => {
const { t } = useForkliftTranslation();

const helperTextMsgs = {
error: (
<span className="forklift-edit-modal-field-error-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
{
'Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and optionally a port. For example: '
}
<ExternalLink href="https://api.<your-openshift-domain>:6443" isInline hideIcon>
https://api.&#8249;your-openshift-domain&#8250;:6443
</ExternalLink>
{' .'}
</Trans>
</span>
),
success: (
<span className="forklift-edit-modal-field-success-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
{'URL of the Openshift Virtualization API endpoint.'}
</Trans>
</span>
),
default: (
<span className="forklift-edit-modal-field-default-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
{'URL of the Openshift Virtualization API endpoint.'}
</Trans>
</span>
),
};

const urlValidationHook: ValidationHookType = (value) => {
const isValidURL = validateURL(value.toString().trim());

return isValidURL
? {
validationHelpText: undefined,
validated: 'success',
}
: {
validationHelpText: t(
'URL must start with https:// or http:// and contain valid hostname and path',
),
validated: 'error',
};
const trimmedUrl: string = value.toString().trim();
const isValidURL = validateURL(trimmedUrl);

// error
if (!isValidURL)
return {
validationHelpText: helperTextMsgs.error,
validated: 'error',
};

// success
return {
validationHelpText: helperTextMsgs.success,
validated: 'success',
};
};

return (
Expand All @@ -37,12 +71,8 @@ export const OpenshiftEditURLModal: React.FC<EditProviderURLModalProps> = (props
label={props?.label || t('URL')}
model={ProviderModel}
variant={ModalVariant.large}
body={t(
'Specify OpenShift cluster API endpoint, for example, https://<kubernetes API Endpoint>:6443 for OpenShift. Empty may be used for the host provider.',
)}
helperText={t(
'Please enter URL for the kubernetes API server, if empty URL default to this cluster.',
)}
body={t('URL of the Openshift Virtualization API endpoint.')}
helperText={helperTextMsgs.default}
validationHook={urlValidationHook}
onConfirmHook={patchProviderURL}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,17 @@
.forklift--create-provider-edit-card-title-logo {
margin-right: var(--pf-global--spacer--sm);
}

.forklift--create-provider-field-error-validation {
color: var(--pf-v5-global--danger-color--100);
font-size: small;
}

.forklift--create-provider-field-success-validation {
color: var(--pf-v5-global--success-color--100);
font-size: small;
}

.forklift--create-provider-field-default-validation {
font-size: small;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useCallback, useReducer } from 'react';
import { Trans } from 'react-i18next';
import { validateURL, Validation } from 'src/modules/Providers/utils';
import { useForkliftTranslation } from 'src/utils/i18n';

import { ExternalLink } from '@kubev2v/common';
import { V1beta1Provider } from '@kubev2v/types';
import { Form, FormGroup, TextInput } from '@patternfly/react-core';

Expand All @@ -18,9 +20,44 @@ export const OpenshiftProviderFormCreate: React.FC<OpenshiftProviderCreateFormPr

const url = provider?.spec?.url || '';

const helperTextMsgs = {
error: (
<span className="forklift--create-provider-field-error-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
{
'Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a domain name, and optionally a port. For example: '
}
<ExternalLink href="https://api.<your-openshift-domain>:6443" isInline hideIcon>
https://api.&#8249;your-openshift-domain&#8250;:6443
</ExternalLink>
{' .'}
</Trans>
</span>
),
success: (
<span className="forklift--create-provider-field-success-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
{
'URL of the Openshift Virtualization API endpoint.<br>If both <strong>URL</strong> and <strong>Service account bearer token</strong> are left blank, the local OpenShift cluster is used.'
}
</Trans>
</span>
),
default: (
<span className="forklift--create-provider-field-default-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
{
'URL of the Openshift Virtualization API endpoint.<br>If both <strong>URL</strong> and <strong>Service account bearer token</strong> are left blank, the local OpenShift cluster is used.'
}
</Trans>
</span>
),
};

const initialState = {
validation: {
url: 'default' as Validation,
urlHelperText: helperTextMsgs.default,
},
};

Expand All @@ -43,27 +80,42 @@ export const OpenshiftProviderFormCreate: React.FC<OpenshiftProviderCreateFormPr

const handleChange = useCallback(
(id, value) => {
const trimmedValue = value.trim();
if (id !== 'url') return;

const trimmedValue: string = value.trim();
const validationState = getURLValidationState(trimmedValue);

if (id === 'url') {
const validationState =
trimmedValue === '' || validateURL(trimmedValue) ? 'success' : 'error';
dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: id, validationState } });
dispatch({
type: 'SET_FIELD_VALIDATED',
payload: { field: 'url', validationState },
});

onChange({ ...provider, spec: { ...provider.spec, url: trimmedValue } });
}
dispatch({
type: 'SET_FIELD_VALIDATED',
payload: {
field: 'urlHelperText',
validationState: helperTextMsgs[validationState],
},
});

onChange({ ...provider, spec: { ...provider.spec, url: trimmedValue } });
},
[provider],
);

const getURLValidationState = (url: string): Validation => {
if (url.length !== 0 && !validateURL(url)) return 'error';
return 'success';
};

return (
<Form isWidthLimited className="forklift-section-provider-edit">
<FormGroup
label={t('URL')}
fieldId="url"
helperText={t('URL of the provider, leave empty to use this providers URL')}
validated={state.validation.url}
helperTextInvalid={t('Error: URL must be valid.')}
helperText={state.validation.urlHelperText}
helperTextInvalid={state.validation.urlHelperText}
>
<TextInput
type="text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,16 @@
padding: var(--pf-global--spacer--xs);
}

.forklift-page-provider-field-error-validation {
color: var(--pf-v5-global--danger-color--100);
font-size: small;
}

.forklift-page-provider-field-success-validation {
color: var(--pf-v5-global--success-color--100);
font-size: small;
}

.forklift-page-provider-field-default-validation {
font-size: small;
}
Loading

0 comments on commit 4c66a1b

Please sign in to comment.