diff --git a/labextension/src/components/Input.tsx b/labextension/src/components/Input.tsx index 167919a3c..a943fd378 100644 --- a/labextension/src/components/Input.tsx +++ b/labextension/src/components/Input.tsx @@ -191,6 +191,8 @@ export interface IInputProps extends Omit< value: string | number; regex?: string; regexErrorMsg?: string; + maxLength?: number; + maxLengthErrorMsg?: string; inputIndex?: number; helperText?: string; readOnly?: boolean; @@ -207,6 +209,8 @@ export const Input: React.FunctionComponent = props => { helperText = null, regex, regexErrorMsg, + maxLength, + maxLengthErrorMsg, validation, placeholder, inputIndex, @@ -251,7 +255,19 @@ export const Input: React.FunctionComponent = props => { regexPattern !== undefined && value !== '' ? !new RegExp(regexPattern).test(value) : false; - const error = regexError || beforeUpdateError; + const maxLengthError = + maxLength !== undefined ? value.length > maxLength : false; + const error = regexError || maxLengthError || beforeUpdateError; + + const getErrorMessage = (): string | undefined => { + if (maxLengthError) { + return ( + maxLengthErrorMsg ?? + `Must be ${maxLength} characters or fewer (currently ${value.length})` + ); + } + return getRegexMessage(); + }; const handleChange = (evt: React.ChangeEvent) => { const newValue = evt.target.value; @@ -268,11 +284,20 @@ export const Input: React.FunctionComponent = props => { variant={variant} className={className} error={error} + sx={ + error + ? { + '& .MuiInputLabel-root': { color: 'error.main' }, + '& .MuiInputBase-input': { color: 'error.main' }, + '& .MuiFormHelperText-root': { color: 'error.main' }, + } + : undefined + } value={value} margin="dense" placeholder={placeholder} spellCheck={false} - helperText={error ? getRegexMessage() : helperText} + helperText={error ? getErrorMessage() : helperText} slotProps={{ input: { readOnly: readOnly, diff --git a/labextension/src/widgets/LeftPanel.tsx b/labextension/src/widgets/LeftPanel.tsx index a91bb9b02..2128ffe0a 100644 --- a/labextension/src/widgets/LeftPanel.tsx +++ b/labextension/src/widgets/LeftPanel.tsx @@ -42,6 +42,7 @@ const KFP_STATUS_REFRESH_MS = 30_000; const KALE_NOTEBOOK_METADATA_KEY = 'kubeflow_notebook'; const DEFAULT_UI_URL = 'http://localhost:8080'; +const PIPELINE_NAME_MAX_LENGTH = 124; export interface IExperiment { id: string; @@ -654,9 +655,10 @@ export class KubeflowKaleLeftPanel extends React.Component { experimentInputValue = selectedExperiments[0].name; } } - const pipelineNameValid = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test( - this.state.metadata.pipeline_name, - ); + const pipelineNameValid = + /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test( + this.state.metadata.pipeline_name, + ) && this.state.metadata.pipeline_name.length <= PIPELINE_NAME_MAX_LENGTH; const experimentNameRegex = /^[a-z]([-a-z0-9]*[a-z0-9])?$/; const experimentNameValid = experimentInputSelected !== NEW_EXPERIMENT.id || @@ -683,6 +685,8 @@ export class KubeflowKaleLeftPanel extends React.Component { regexErrorMsg={ "Pipeline name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character." } + maxLength={PIPELINE_NAME_MAX_LENGTH} + maxLengthErrorMsg={`Pipeline name must be ${PIPELINE_NAME_MAX_LENGTH} characters or fewer.`} /> );