1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Add SystemName validation to AccessibleNamespaces (#2754)

- Don't validate after a submit

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2021-07-12 16:13:24 -04:00 committed by GitHub
parent c6618bde88
commit 485783167d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 92 additions and 50 deletions

View File

@ -25,6 +25,7 @@ import type { Cluster } from "../../../../main/cluster";
import { SubTitle } from "../../layout/sub-title";
import { EditableList } from "../../editable-list";
import { observable, makeObservable } from "mobx";
import { systemName } from "../../input/input_validators";
interface Props {
cluster: Cluster;
@ -49,6 +50,7 @@ export class ClusterAccessibleNamespaces extends React.Component<Props> {
this.namespaces.add(newNamespace);
this.props.cluster.accessibleNamespaces = Array.from(this.namespaces);
}}
validators={systemName}
items={Array.from(this.namespaces)}
remove={({ oldItem: oldNamesapce }) => {
this.namespaces.delete(oldNamesapce);

View File

@ -20,8 +20,13 @@
*/
.EditableList {
--gradientColor: var(--colorVague);
.el-contents {
display: flex;
background-color: var(--colorVague);
color: $textColorSecondary;
border-radius: $radius;
flex-direction: column;
&:not(:empty) {
@ -35,20 +40,53 @@
}
.el-item {
display: grid;
grid-template-columns: 1fr auto;
padding: $padding $padding * 2;
margin-bottom: $padding / 4;
backdrop-filter: brightness(0.75);
border-radius: var(--border-radius);
display: flex;
flex-direction: row;
padding: $padding 0;
margin-bottom: 1px;
:last-child {
margin-bottom: unset;
}
:first-child {
.el-value-container {
position: relative;
max-width: calc(100% - 29px);
align-self: center;
&::before, &::after {
content: ' ';
position: absolute;
z-index: 20;
display: block;
width: 8px;
height: var(--font-size);
}
&::before {
left: 0px;
top: 0px;
background: linear-gradient(to right, var(--gradientColor) 0px, transparent);
}
&::after {
right: 0px;
top: 0px;
background: linear-gradient(to left, var(--gradientColor) 0px, transparent);
}
}
.el-value {
white-space: nowrap;
overflow: scroll!important;
text-overflow: unset!important;
padding-left: 8px;
padding-right: 8px;
&::-webkit-scrollbar {
display: none;
}
}
}
:last-child {
margin-bottom: unset;
}
}
}

View File

@ -25,7 +25,7 @@ import { observer } from "mobx-react";
import React from "react";
import { Icon } from "../icon";
import { Input, InputProps } from "../input";
import { Input, InputProps, InputValidator } from "../input";
import { boundMethod } from "../../utils";
export interface Props<T> {
@ -33,6 +33,7 @@ export interface Props<T> {
add: (newItem: string) => void,
remove: (info: { oldItem: T, index: number }) => void,
placeholder?: string,
validators?: InputValidator | InputValidator[];
// An optional prop used to convert T to a displayable string
// defaults to `String`
@ -61,7 +62,7 @@ export class EditableList<T> extends React.Component<Props<T>> {
}
render() {
const { items, remove, renderItem, placeholder, inputTheme } = this.props;
const { items, remove, renderItem, placeholder, validators, inputTheme } = this.props;
return (
<div className="EditableList">
@ -69,6 +70,7 @@ export class EditableList<T> extends React.Component<Props<T>> {
<Input
theme={inputTheme}
onSubmit={this.onSubmit}
validators={validators}
placeholder={placeholder}
/>
</div>
@ -76,7 +78,9 @@ export class EditableList<T> extends React.Component<Props<T>> {
{
items.map((item, index) => (
<div key={`${item}${index}`} className="el-item">
<div>{renderItem(item, index)}</div>
<div className="el-value-container">
<div className="el-value">{renderItem(item, index)}</div>
</div>
<div className="el-value-remove">
<Icon material="delete_outline" onClick={() => remove(({ index, oldItem: item }))} />
</div>

View File

@ -31,6 +31,7 @@ import isString from "lodash/isString";
import isFunction from "lodash/isFunction";
import isBoolean from "lodash/isBoolean";
import uniqueId from "lodash/uniqueId";
import { debounce } from "lodash";
const { conditionalValidators, ...InputValidators } = Validators;
@ -59,12 +60,12 @@ export type InputProps<T = string> = Omit<InputElementProps, "onChange" | "onSub
};
interface State {
focused?: boolean;
dirty?: boolean;
dirtyOnBlur?: boolean;
valid?: boolean;
validating?: boolean;
errors?: React.ReactNode[];
focused: boolean;
dirty: boolean;
valid: boolean;
validating: boolean;
errors: React.ReactNode[];
submitted: boolean;
}
const defaultProps: Partial<InputProps> = {
@ -81,15 +82,14 @@ export class Input extends React.Component<InputProps, State> {
public validators: InputValidator[] = [];
public state: State = {
dirty: !!this.props.dirty,
focused: false,
valid: true,
validating: false,
dirty: !!this.props.dirty,
errors: [],
submitted: false,
};
isValid() {
return this.state.valid;
}
setValue(value = "") {
if (value !== this.getValue()) {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(this.input.constructor.prototype, "value").set;
@ -213,7 +213,6 @@ export class Input extends React.Component<InputProps, State> {
}
setDirty(dirty = true) {
if (this.state.dirty === dirty) return;
this.setState({ dirty });
}
@ -221,30 +220,25 @@ export class Input extends React.Component<InputProps, State> {
onFocus(evt: React.FocusEvent<InputElement>) {
const { onFocus, autoSelectOnFocus } = this.props;
if (onFocus) onFocus(evt);
onFocus?.(evt);
if (autoSelectOnFocus) this.select();
this.setState({ focused: true });
}
@boundMethod
onBlur(evt: React.FocusEvent<InputElement>) {
const { onBlur } = this.props;
if (onBlur) onBlur(evt);
if (this.state.dirtyOnBlur) this.setState({ dirty: true, dirtyOnBlur: false });
this.props.onBlur?.(evt);
this.setState({ focused: false });
}
setDirtyOnChange = debounce(() => this.setDirty(), 500);
@boundMethod
onChange(evt: React.ChangeEvent<InputElement>) {
onChange(evt: React.ChangeEvent<any>) {
this.props.onChange?.(evt.currentTarget.value, evt);
this.validate();
this.autoFitHeight();
// mark input as dirty for the first time only onBlur() to avoid immediate error-state show when start typing
if (!this.state.dirty) {
this.setState({ dirtyOnBlur: true });
}
this.setDirtyOnChange();
// re-render component when used as uncontrolled input
// when used @defaultValue instead of @value changing real input.value doesn't call render()
@ -255,20 +249,20 @@ export class Input extends React.Component<InputProps, State> {
@boundMethod
onKeyDown(evt: React.KeyboardEvent<InputElement>) {
const modified = evt.shiftKey || evt.metaKey || evt.altKey || evt.ctrlKey;
this.props.onKeyDown?.(evt);
switch (evt.key) {
case "Enter":
if (this.props.onSubmit && !modified && !evt.repeat && this.isValid()) {
this.props.onSubmit(this.getValue(), evt);
if (evt.shiftKey || evt.metaKey || evt.altKey || evt.ctrlKey || evt.repeat) {
return;
}
if (this.isUncontrolled) {
this.setValue();
}
}
break;
if (evt.key === "Enter") {
if (this.state.valid) {
this.props.onSubmit?.(this.getValue(), evt);
this.setDirtyOnChange.cancel();
this.setState({ submitted: true });
} else {
this.setDirty();
}
}
}
@ -291,7 +285,11 @@ export class Input extends React.Component<InputProps, State> {
const { defaultValue, value, dirty, validators } = this.props;
if (prevProps.value !== value || defaultValue !== prevProps.defaultValue) {
this.validate();
if (!this.state.submitted) {
this.validate();
} else {
this.setState({ submitted: false });
}
this.autoFitHeight();
}