-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support better K8s Resource Name editing
- Loading branch information
1 parent
e344d82
commit 6364a1b
Showing
22 changed files
with
904 additions
and
154 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import * as _ from 'lodash-es'; | ||
import { RecursivePartial } from '~/typeHelpers'; | ||
import { K8sNameDescriptionFieldData } from '~/concepts/k8s/K8sNameDescriptionField/types'; | ||
|
||
export const mockK8sNameDescriptionFieldData = ( | ||
overrides: RecursivePartial<K8sNameDescriptionFieldData> = {}, | ||
): K8sNameDescriptionFieldData => | ||
_.merge( | ||
{}, | ||
{ | ||
name: '', | ||
description: '', | ||
k8sName: { | ||
value: '', | ||
state: { | ||
immutable: false, | ||
invalidLength: false, | ||
invalidCharacters: false, | ||
autoTrimmed: false, | ||
maxLength: 253, | ||
touched: false, | ||
}, | ||
}, | ||
}, | ||
overrides, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
frontend/src/concepts/k8s/K8sNameDescriptionField/HelperTextItemVariants.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import * as React from 'react'; | ||
import { HelperTextItem } from '@patternfly/react-core'; | ||
import { K8sNameDescriptionFieldData } from '~/concepts/k8s/K8sNameDescriptionField/types'; | ||
|
||
type Variants = React.ComponentProps<typeof HelperTextItem>['variant']; | ||
|
||
type HelperTextItemType = React.FC<{ | ||
k8sName: K8sNameDescriptionFieldData['k8sName']; | ||
}>; | ||
|
||
export const HelperTextItemMaxLength: HelperTextItemType = ({ k8sName }) => { | ||
let variant: Variants = 'indeterminate'; | ||
if (k8sName.state.invalidLength) { | ||
variant = 'error'; | ||
} else if (k8sName.state.autoTrimmed) { | ||
variant = 'warning'; | ||
} else if (k8sName.value.trim().length > 0) { | ||
variant = 'success'; | ||
} | ||
|
||
return ( | ||
<HelperTextItem variant={variant} hasIcon> | ||
Cannot exceed {k8sName.state.maxLength} characters | ||
</HelperTextItem> | ||
); | ||
}; | ||
|
||
export const HelperTextItemValidCharacters: HelperTextItemType = ({ k8sName }) => { | ||
let variant: Variants = 'indeterminate'; | ||
if (k8sName.state.invalidCharacters) { | ||
variant = 'error'; | ||
} else if (k8sName.value.trim().length > 0) { | ||
variant = 'success'; | ||
} | ||
|
||
return ( | ||
<HelperTextItem variant={variant} hasIcon> | ||
Must start and end with a letter or number. Valid characters include lowercase letters, | ||
numbers, and hyphens (-). | ||
</HelperTextItem> | ||
); | ||
}; |
128 changes: 128 additions & 0 deletions
128
frontend/src/concepts/k8s/K8sNameDescriptionField/K8sNameDescriptionField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import * as React from 'react'; | ||
import { | ||
Button, | ||
FormGroup, | ||
FormHelperText, | ||
HelperText, | ||
HelperTextItem, | ||
Stack, | ||
StackItem, | ||
TextArea, | ||
TextInput, | ||
} from '@patternfly/react-core'; | ||
import { | ||
K8sNameDescriptionFieldData, | ||
K8sNameDescriptionFieldUpdateFunction, | ||
UseK8sNameDescriptionDataConfiguration, | ||
UseK8sNameDescriptionFieldData, | ||
} from '~/concepts/k8s/K8sNameDescriptionField/types'; | ||
import ResourceNameDefinitionTooltip from '~/concepts/k8s/ResourceNameDefinitionTooltip'; | ||
import useAutoFocusRef from '~/utilities/useAutoFocusRef'; | ||
import { handleUpdateLogic, setupDefaults } from '~/concepts/k8s/K8sNameDescriptionField/utils'; | ||
import ResourceNameField from '~/concepts/k8s/K8sNameDescriptionField/ResourceNameField'; | ||
|
||
/** Companion data hook */ | ||
export const useK8sNameDescriptionFieldData = ( | ||
configuration: UseK8sNameDescriptionDataConfiguration = {}, | ||
): UseK8sNameDescriptionFieldData => { | ||
const [data, setData] = React.useState<K8sNameDescriptionFieldData>(() => | ||
setupDefaults(configuration), | ||
); | ||
|
||
// Hold the data in a ref to avoid churn in the update method | ||
const dataRef = React.useRef(data); | ||
dataRef.current = data; | ||
const onDataChange = React.useCallback<K8sNameDescriptionFieldUpdateFunction>( | ||
(key, value) => { | ||
setData(handleUpdateLogic(dataRef.current)(key, value)); | ||
}, | ||
[setData], | ||
); | ||
|
||
return { data, onDataChange }; | ||
}; | ||
|
||
type K8sNameDescriptionFieldProps = { | ||
autoFocusName?: boolean; | ||
dataTestId: string; | ||
descriptionLabel?: string; | ||
nameLabel?: string; | ||
} & UseK8sNameDescriptionFieldData; | ||
|
||
/** | ||
* Use in place of any K8s Resource creation / edit. | ||
* @see useK8sNameDescriptionFieldData | ||
*/ | ||
const K8sNameDescriptionField: React.FC<K8sNameDescriptionFieldProps> = ({ | ||
autoFocusName, | ||
data, | ||
dataTestId, | ||
descriptionLabel = 'Description', | ||
onDataChange, | ||
nameLabel = 'Name', | ||
}) => { | ||
const autoFocusNameRef = useAutoFocusRef(autoFocusName); | ||
const [showK8sField, setShowK8sField] = React.useState(false); | ||
|
||
const { name, description, k8sName } = data; | ||
|
||
return ( | ||
<Stack hasGutter> | ||
<StackItem> | ||
<FormGroup label={nameLabel} isRequired> | ||
<TextInput | ||
data-testid={`${dataTestId}-name`} | ||
id={`${dataTestId}-name`} | ||
ref={autoFocusNameRef} | ||
isRequired | ||
value={name} | ||
onChange={(event, value) => onDataChange('name', value)} | ||
/> | ||
</FormGroup> | ||
{!showK8sField && !k8sName.state.immutable && ( | ||
<FormHelperText> | ||
{k8sName.value && ( | ||
<HelperText> | ||
<HelperTextItem> | ||
The resource name will be <b>{k8sName.value}</b>. | ||
</HelperTextItem> | ||
</HelperText> | ||
)} | ||
<HelperText> | ||
<HelperTextItem> | ||
<Button | ||
data-testid={`${dataTestId}-editResourceLink`} | ||
variant="link" | ||
isInline | ||
onClick={() => setShowK8sField(true)} | ||
> | ||
Edit resource name | ||
</Button>{' '} | ||
<ResourceNameDefinitionTooltip /> | ||
</HelperTextItem> | ||
</HelperText> | ||
</FormHelperText> | ||
)} | ||
</StackItem> | ||
<ResourceNameField | ||
allowEdit={showK8sField} | ||
dataTestId={dataTestId} | ||
k8sName={k8sName} | ||
onDataChange={onDataChange} | ||
/> | ||
<StackItem> | ||
<FormGroup label={descriptionLabel}> | ||
<TextArea | ||
data-testid={`${dataTestId}-description`} | ||
id={`${dataTestId}-description`} | ||
value={description} | ||
onChange={(event, value) => onDataChange('description', value)} | ||
resizeOrientation="vertical" | ||
/> | ||
</FormGroup> | ||
</StackItem> | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default K8sNameDescriptionField; |
58 changes: 58 additions & 0 deletions
58
frontend/src/concepts/k8s/K8sNameDescriptionField/ResourceNameField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import * as React from 'react'; | ||
import { FormGroup, HelperText, StackItem, TextInput } from '@patternfly/react-core'; | ||
import ResourceNameDefinitionTooltip from '~/concepts/k8s/ResourceNameDefinitionTooltip'; | ||
import { | ||
HelperTextItemMaxLength, | ||
HelperTextItemValidCharacters, | ||
} from '~/concepts/k8s/K8sNameDescriptionField/HelperTextItemVariants'; | ||
import { | ||
K8sNameDescriptionFieldData, | ||
K8sNameDescriptionFieldUpdateFunction, | ||
} from '~/concepts/k8s/K8sNameDescriptionField/types'; | ||
|
||
type ResourceNameFieldProps = { | ||
allowEdit: boolean; | ||
dataTestId: string; | ||
k8sName: K8sNameDescriptionFieldData['k8sName']; | ||
onDataChange: K8sNameDescriptionFieldUpdateFunction; | ||
}; | ||
|
||
/** Sub-resource; not for public cunsumption */ | ||
const ResourceNameField: React.FC<ResourceNameFieldProps> = ({ | ||
allowEdit, | ||
dataTestId, | ||
k8sName, | ||
onDataChange, | ||
}) => { | ||
if (k8sName.state.immutable) { | ||
return ( | ||
<StackItem> | ||
<FormGroup label="Resource name" labelIcon={<ResourceNameDefinitionTooltip />}> | ||
{k8sName.value} | ||
</FormGroup> | ||
</StackItem> | ||
); | ||
} | ||
|
||
if (!allowEdit) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<StackItem> | ||
<FormGroup label="Resource name" labelIcon={<ResourceNameDefinitionTooltip />}> | ||
<TextInput | ||
data-testid={`${dataTestId}-resourceName`} | ||
value={k8sName.value} | ||
onChange={(event, value) => onDataChange('k8sName', value)} | ||
/> | ||
<HelperText> | ||
<HelperTextItemMaxLength k8sName={k8sName} /> | ||
<HelperTextItemValidCharacters k8sName={k8sName} /> | ||
</HelperText> | ||
</FormGroup> | ||
</StackItem> | ||
); | ||
}; | ||
|
||
export default ResourceNameField; |
Oops, something went wrong.