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:
parent
c6618bde88
commit
485783167d
@ -25,6 +25,7 @@ import type { Cluster } from "../../../../main/cluster";
|
|||||||
import { SubTitle } from "../../layout/sub-title";
|
import { SubTitle } from "../../layout/sub-title";
|
||||||
import { EditableList } from "../../editable-list";
|
import { EditableList } from "../../editable-list";
|
||||||
import { observable, makeObservable } from "mobx";
|
import { observable, makeObservable } from "mobx";
|
||||||
|
import { systemName } from "../../input/input_validators";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
cluster: Cluster;
|
cluster: Cluster;
|
||||||
@ -49,6 +50,7 @@ export class ClusterAccessibleNamespaces extends React.Component<Props> {
|
|||||||
this.namespaces.add(newNamespace);
|
this.namespaces.add(newNamespace);
|
||||||
this.props.cluster.accessibleNamespaces = Array.from(this.namespaces);
|
this.props.cluster.accessibleNamespaces = Array.from(this.namespaces);
|
||||||
}}
|
}}
|
||||||
|
validators={systemName}
|
||||||
items={Array.from(this.namespaces)}
|
items={Array.from(this.namespaces)}
|
||||||
remove={({ oldItem: oldNamesapce }) => {
|
remove={({ oldItem: oldNamesapce }) => {
|
||||||
this.namespaces.delete(oldNamesapce);
|
this.namespaces.delete(oldNamesapce);
|
||||||
|
|||||||
@ -20,8 +20,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.EditableList {
|
.EditableList {
|
||||||
|
--gradientColor: var(--colorVague);
|
||||||
|
|
||||||
.el-contents {
|
.el-contents {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
background-color: var(--colorVague);
|
||||||
|
color: $textColorSecondary;
|
||||||
|
border-radius: $radius;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
&:not(:empty) {
|
&:not(:empty) {
|
||||||
@ -35,20 +40,53 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-item {
|
.el-item {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 1fr auto;
|
flex-direction: row;
|
||||||
padding: $padding $padding * 2;
|
padding: $padding 0;
|
||||||
margin-bottom: $padding / 4;
|
margin-bottom: 1px;
|
||||||
backdrop-filter: brightness(0.75);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
|
|
||||||
:last-child {
|
.el-value-container {
|
||||||
margin-bottom: unset;
|
position: relative;
|
||||||
}
|
max-width: calc(100% - 29px);
|
||||||
|
|
||||||
:first-child {
|
|
||||||
align-self: center;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import { observer } from "mobx-react";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { Input, InputProps } from "../input";
|
import { Input, InputProps, InputValidator } from "../input";
|
||||||
import { boundMethod } from "../../utils";
|
import { boundMethod } from "../../utils";
|
||||||
|
|
||||||
export interface Props<T> {
|
export interface Props<T> {
|
||||||
@ -33,6 +33,7 @@ export interface Props<T> {
|
|||||||
add: (newItem: string) => void,
|
add: (newItem: string) => void,
|
||||||
remove: (info: { oldItem: T, index: number }) => void,
|
remove: (info: { oldItem: T, index: number }) => void,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
|
validators?: InputValidator | InputValidator[];
|
||||||
|
|
||||||
// An optional prop used to convert T to a displayable string
|
// An optional prop used to convert T to a displayable string
|
||||||
// defaults to `String`
|
// defaults to `String`
|
||||||
@ -61,7 +62,7 @@ export class EditableList<T> extends React.Component<Props<T>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { items, remove, renderItem, placeholder, inputTheme } = this.props;
|
const { items, remove, renderItem, placeholder, validators, inputTheme } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="EditableList">
|
<div className="EditableList">
|
||||||
@ -69,6 +70,7 @@ export class EditableList<T> extends React.Component<Props<T>> {
|
|||||||
<Input
|
<Input
|
||||||
theme={inputTheme}
|
theme={inputTheme}
|
||||||
onSubmit={this.onSubmit}
|
onSubmit={this.onSubmit}
|
||||||
|
validators={validators}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -76,7 +78,9 @@ export class EditableList<T> extends React.Component<Props<T>> {
|
|||||||
{
|
{
|
||||||
items.map((item, index) => (
|
items.map((item, index) => (
|
||||||
<div key={`${item}${index}`} className="el-item">
|
<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">
|
<div className="el-value-remove">
|
||||||
<Icon material="delete_outline" onClick={() => remove(({ index, oldItem: item }))} />
|
<Icon material="delete_outline" onClick={() => remove(({ index, oldItem: item }))} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import isString from "lodash/isString";
|
|||||||
import isFunction from "lodash/isFunction";
|
import isFunction from "lodash/isFunction";
|
||||||
import isBoolean from "lodash/isBoolean";
|
import isBoolean from "lodash/isBoolean";
|
||||||
import uniqueId from "lodash/uniqueId";
|
import uniqueId from "lodash/uniqueId";
|
||||||
|
import { debounce } from "lodash";
|
||||||
|
|
||||||
const { conditionalValidators, ...InputValidators } = Validators;
|
const { conditionalValidators, ...InputValidators } = Validators;
|
||||||
|
|
||||||
@ -59,12 +60,12 @@ export type InputProps<T = string> = Omit<InputElementProps, "onChange" | "onSub
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
focused?: boolean;
|
focused: boolean;
|
||||||
dirty?: boolean;
|
dirty: boolean;
|
||||||
dirtyOnBlur?: boolean;
|
valid: boolean;
|
||||||
valid?: boolean;
|
validating: boolean;
|
||||||
validating?: boolean;
|
errors: React.ReactNode[];
|
||||||
errors?: React.ReactNode[];
|
submitted: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps: Partial<InputProps> = {
|
const defaultProps: Partial<InputProps> = {
|
||||||
@ -81,15 +82,14 @@ export class Input extends React.Component<InputProps, State> {
|
|||||||
public validators: InputValidator[] = [];
|
public validators: InputValidator[] = [];
|
||||||
|
|
||||||
public state: State = {
|
public state: State = {
|
||||||
dirty: !!this.props.dirty,
|
focused: false,
|
||||||
valid: true,
|
valid: true,
|
||||||
|
validating: false,
|
||||||
|
dirty: !!this.props.dirty,
|
||||||
errors: [],
|
errors: [],
|
||||||
|
submitted: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
isValid() {
|
|
||||||
return this.state.valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(value = "") {
|
setValue(value = "") {
|
||||||
if (value !== this.getValue()) {
|
if (value !== this.getValue()) {
|
||||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(this.input.constructor.prototype, "value").set;
|
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) {
|
setDirty(dirty = true) {
|
||||||
if (this.state.dirty === dirty) return;
|
|
||||||
this.setState({ dirty });
|
this.setState({ dirty });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,30 +220,25 @@ export class Input extends React.Component<InputProps, State> {
|
|||||||
onFocus(evt: React.FocusEvent<InputElement>) {
|
onFocus(evt: React.FocusEvent<InputElement>) {
|
||||||
const { onFocus, autoSelectOnFocus } = this.props;
|
const { onFocus, autoSelectOnFocus } = this.props;
|
||||||
|
|
||||||
if (onFocus) onFocus(evt);
|
onFocus?.(evt);
|
||||||
if (autoSelectOnFocus) this.select();
|
if (autoSelectOnFocus) this.select();
|
||||||
this.setState({ focused: true });
|
this.setState({ focused: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
onBlur(evt: React.FocusEvent<InputElement>) {
|
onBlur(evt: React.FocusEvent<InputElement>) {
|
||||||
const { onBlur } = this.props;
|
this.props.onBlur?.(evt);
|
||||||
|
|
||||||
if (onBlur) onBlur(evt);
|
|
||||||
if (this.state.dirtyOnBlur) this.setState({ dirty: true, dirtyOnBlur: false });
|
|
||||||
this.setState({ focused: false });
|
this.setState({ focused: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDirtyOnChange = debounce(() => this.setDirty(), 500);
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
onChange(evt: React.ChangeEvent<InputElement>) {
|
onChange(evt: React.ChangeEvent<any>) {
|
||||||
this.props.onChange?.(evt.currentTarget.value, evt);
|
this.props.onChange?.(evt.currentTarget.value, evt);
|
||||||
this.validate();
|
this.validate();
|
||||||
this.autoFitHeight();
|
this.autoFitHeight();
|
||||||
|
this.setDirtyOnChange();
|
||||||
// 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 });
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-render component when used as uncontrolled input
|
// re-render component when used as uncontrolled input
|
||||||
// when used @defaultValue instead of @value changing real input.value doesn't call render()
|
// 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
|
@boundMethod
|
||||||
onKeyDown(evt: React.KeyboardEvent<InputElement>) {
|
onKeyDown(evt: React.KeyboardEvent<InputElement>) {
|
||||||
const modified = evt.shiftKey || evt.metaKey || evt.altKey || evt.ctrlKey;
|
|
||||||
|
|
||||||
this.props.onKeyDown?.(evt);
|
this.props.onKeyDown?.(evt);
|
||||||
|
|
||||||
switch (evt.key) {
|
if (evt.shiftKey || evt.metaKey || evt.altKey || evt.ctrlKey || evt.repeat) {
|
||||||
case "Enter":
|
return;
|
||||||
if (this.props.onSubmit && !modified && !evt.repeat && this.isValid()) {
|
}
|
||||||
this.props.onSubmit(this.getValue(), evt);
|
|
||||||
|
|
||||||
if (this.isUncontrolled) {
|
if (evt.key === "Enter") {
|
||||||
this.setValue();
|
if (this.state.valid) {
|
||||||
}
|
this.props.onSubmit?.(this.getValue(), evt);
|
||||||
}
|
this.setDirtyOnChange.cancel();
|
||||||
break;
|
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;
|
const { defaultValue, value, dirty, validators } = this.props;
|
||||||
|
|
||||||
if (prevProps.value !== value || defaultValue !== prevProps.defaultValue) {
|
if (prevProps.value !== value || defaultValue !== prevProps.defaultValue) {
|
||||||
this.validate();
|
if (!this.state.submitted) {
|
||||||
|
this.validate();
|
||||||
|
} else {
|
||||||
|
this.setState({ submitted: false });
|
||||||
|
}
|
||||||
this.autoFitHeight();
|
this.autoFitHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user