feat(core-blocks): CB-872 add default values support

This commit is contained in:
Naumov Alexey
2021-04-20 10:32:07 +03:00
parent 83e229ea8e
commit 2c9e838ee7
14 changed files with 99 additions and 77 deletions
@@ -21,9 +21,11 @@ export interface CheckboxBaseProps {
ripple?: boolean;
indeterminate?: boolean;
style?: ComponentStyle;
defaultChecked?: boolean;
defaultValue?: string;
}
export type CheckboxInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'type' | 'value' | 'checked' | 'id' | 'style'> & ILayoutSizeProps;
export type CheckboxInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'type' | 'value' | 'defaultValue' | 'checked' | 'defaultChecked' | 'id' | 'style'> & ILayoutSizeProps;
export interface ICheckboxControlledProps extends CheckboxInputProps {
value?: string;
@@ -35,7 +37,7 @@ export interface ICheckboxControlledProps extends CheckboxInputProps {
export interface ICheckboxObjectProps<TKey extends string> extends CheckboxInputProps {
value?: string;
state: Partial<Record<TKey, boolean | null | string | string[]>>;
state?: Partial<Record<TKey, boolean | null | string | string[]>>;
checked?: never;
onChange?: CheckboxOnChangeEvent<TKey>;
autoHide?: boolean;
@@ -50,9 +52,11 @@ export interface CheckboxType {
export const Checkbox: CheckboxType = observer(function Checkbox({
name,
value,
defaultValue,
state,
label,
checked,
defaultChecked,
children,
mod,
ripple,
@@ -63,14 +67,15 @@ export const Checkbox: CheckboxType = observer(function Checkbox({
}: CheckboxBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps<any>)) {
const checkboxState = useCheckboxState({
value,
defaultValue,
checked,
defaultChecked: rest.defaultChecked,
defaultChecked,
state,
name,
onChange,
});
if (autoHide && !isControlPresented(name, state)) {
if (autoHide && !isControlPresented(name, state, defaultChecked)) {
return null;
}
@@ -27,7 +27,7 @@ export const FieldCheckbox: CheckboxType = function FieldCheckbox({
}: CheckboxBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps<any>)) {
const styles = useStyles(baseFormControlStyles, fieldCheckboxStyles);
if (rest.autoHide && !isControlPresented(rest.name, rest.state)) {
if (rest.autoHide && !isControlPresented(rest.name, rest.state, rest.defaultChecked)) {
return null;
}
@@ -39,7 +39,7 @@ export const FieldCheckboxNew: CheckboxType = function FieldCheckboxNew({
}: CheckboxBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps<any>)) {
const styles = useStyles(baseFormControlStylesNew, fieldCheckboxStyles);
if (rest.autoHide && !isControlPresented(rest.name, rest.state)) {
if (rest.autoHide && !isControlPresented(rest.name, rest.state, rest.defaultChecked)) {
return null;
}
@@ -81,6 +81,8 @@ interface IBaseProps {
mod?: Array<keyof typeof switchMod>;
label?: string;
description?: string;
defaultValue?: string;
defaultChecked?: boolean;
}
interface SwitchType {
@@ -91,10 +93,12 @@ interface SwitchType {
export const Switch: SwitchType = observer(function Switch({
name,
value,
defaultValue,
label,
description,
state,
checked,
defaultChecked,
className,
children,
onChange,
@@ -105,8 +109,9 @@ export const Switch: SwitchType = observer(function Switch({
}: IBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps<any>)) {
const checkboxState = useCheckboxState({
value,
defaultValue,
checked,
defaultChecked: rest.defaultChecked,
defaultChecked,
state,
name,
onChange,
@@ -119,7 +124,7 @@ export const Switch: SwitchType = observer(function Switch({
checkboxState.checked && switchState.checked
);
if (autoHide && !isControlPresented(name, state)) {
if (autoHide && !isControlPresented(name, state, defaultChecked)) {
return null;
}
@@ -85,6 +85,8 @@ const switchState = {
interface IBaseProps {
mod?: Array<keyof typeof switchMod>;
description?: string;
defaultValue?: string;
defaultChecked?: boolean;
}
interface SwitchType {
@@ -95,9 +97,11 @@ interface SwitchType {
export const SwitchNew: SwitchType = observer(function SwitchNew({
name,
value,
defaultValue,
description,
state,
checked,
defaultChecked,
className,
children,
onChange,
@@ -108,8 +112,9 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({
}: IBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps<any>)) {
const checkboxState = useCheckboxState({
value,
defaultValue,
checked,
defaultChecked: rest.defaultChecked,
defaultChecked,
state,
name,
onChange,
@@ -122,7 +127,7 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({
checkboxState.checked && switchState.checked
);
if (autoHide && !isControlPresented(name, state)) {
if (autoHide && !isControlPresented(name, state, defaultChecked)) {
return null;
}
@@ -15,6 +15,7 @@ export type CheckboxOnChangeEvent<T> = (value: boolean, name: T) => void;
export type CheckboxStateOptions<TKey extends string> = {
value: string | undefined;
defaultValue: string | undefined;
checked: boolean | undefined;
defaultChecked: boolean | undefined;
} & (
@@ -39,22 +40,21 @@ export function useCheckboxState<TKey extends string>(options: CheckboxStateOpti
const [count, refresh] = useState(0);
const context = useContext(FormContext);
const optionsRef = useObjectRef({ ...options, context, count });
const { state, name, value } = optionsRef;
const { state, name } = optionsRef;
let checked = optionsRef.checked ?? false;
let checked = optionsRef.checked ?? optionsRef.defaultChecked ?? false;
const value = optionsRef.value ?? optionsRef.defaultValue ?? undefined;
if (state !== undefined && name !== undefined && name in state) {
const currentState = state[name as TKey];
if (typeof value === 'string' && Array.isArray(currentState)) {
checked = currentState.includes(value);
if (typeof value === 'string') {
checked = Array.isArray(currentState) ? currentState.includes(value) : currentState === value;
} else if (typeof currentState === 'string') {
checked = currentState.toLowerCase() === 'true';
} else {
checked = !!currentState;
}
} else if (optionsRef.defaultChecked !== undefined) {
checked = optionsRef.defaultChecked;
}
return useObjectRef({
@@ -97,10 +97,11 @@ const styles = composes(
`
);
type BaseProps<TKey, TValue> = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'onSelect' | 'name' | 'value'> & ILayoutSizeProps & {
type BaseProps<TKey, TValue> = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'onSelect' | 'name' | 'value' | 'defaultValue'> & ILayoutSizeProps & {
propertyName?: string;
items: TValue[];
searchable?: boolean;
defaultValue?: string;
keySelector: (item: TValue) => TKey;
valueSelector: (item: TValue) => string;
onSwitch?: (state: boolean) => void;
@@ -116,7 +117,7 @@ type ControlledProps<TKey, TValue> = BaseProps<TKey, TValue> & {
type ObjectProps<TValue, TKey extends keyof TState, TState> = BaseProps<TState[TKey], TValue> & {
name: TKey;
state: TState;
state?: TState;
onSelect?: (value: TState[TKey], name: TKey | undefined, prev: TState[TKey]) => void;
onChange?: (value: string, name: TKey | undefined) => any;
value?: never;
@@ -129,6 +130,7 @@ interface ComboboxType {
export const ComboboxNew: ComboboxType = observer(function ComboboxNew({
value: controlledValue,
defaultValue,
name,
state,
propertyName,
@@ -154,14 +156,10 @@ export const ComboboxNew: ComboboxType = observer(function ComboboxNew({
gutter: 4,
});
const [searchValue, setSearchValue] = useState('');
let value: string | number | readonly string[] | undefined = controlledValue;
let value: string | number | readonly string[] | undefined = controlledValue ?? defaultValue ?? undefined;
if (state) {
if (name in state) {
value = state[name];
} else if (rest.defaultValue !== undefined) {
value = rest.defaultValue;
}
if (state && name !== undefined && name in state) {
value = state[name];
}
const selectedItem = items.find(item => keySelector(item) === value);
@@ -16,18 +16,18 @@ import { baseFormControlStyles } from './baseFormControlStyles';
import { FormContext } from './FormContext';
import { isControlPresented } from './isControlPresented';
type BaseProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'name' | 'value'> & {
type BaseProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'name' | 'value' | 'defaultValue'> & {
description?: string;
mod?: 'surface';
long?: boolean;
short?: boolean;
defaultValue?: string;
};
type ControlledProps = BaseProps & {
name?: string;
value?: string;
onChange?: (value: string, name?: string) => any;
state?: never;
autoHide?: never;
};
@@ -37,7 +37,6 @@ type ObjectProps<TKey extends keyof TState, TState> = BaseProps & {
state: TState;
onChange?: (value: string, name: TKey) => any;
autoHide?: boolean;
value?: never;
};
@@ -49,6 +48,7 @@ interface InputFieldType {
export const InputField: InputFieldType = observer(function InputField({
name,
value: valueControlled,
defaultValue,
required,
state,
children,
@@ -76,17 +76,13 @@ export const InputField: InputFieldType = observer(function InputField({
}
}, [state, name, context, onChange]);
let value: any = valueControlled;
let value: any = valueControlled ?? defaultValue ?? undefined;
if (state) {
if (name in state) {
value = state[name];
} else if (rest.defaultValue !== undefined) {
value = rest.defaultValue;
}
if (state && name !== undefined && name in state) {
value = state[name];
}
if (autoHide && !isControlPresented(name, state)) {
if (autoHide && !isControlPresented(name, state, defaultValue)) {
return null;
}
@@ -28,11 +28,12 @@ const INPUT_FIELD_STYLES = css`
}
`;
type BaseProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'name' | 'value'> & ILayoutSizeProps & {
type BaseProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'name' | 'value' | 'defaultValue'> & ILayoutSizeProps & {
description?: string;
mod?: 'surface';
ref?: React.Ref<HTMLInputElement>;
style?: ComponentStyle;
defaultValue?: string | number;
};
type ControlledProps = BaseProps & {
@@ -47,7 +48,7 @@ type ControlledProps = BaseProps & {
type ObjectProps<TKey extends keyof TState, TState> = BaseProps & {
name: TKey;
state: TState;
state?: TState;
mapState?: (value: TState[TKey]) => TState[TKey];
mapValue?: (value: TState[TKey]) => TState[TKey];
onChange?: (value: TState[TKey], name: TKey) => any;
@@ -64,6 +65,7 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({
name,
style,
value: valueControlled,
defaultValue,
required,
state,
mapState,
@@ -96,18 +98,14 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({
}
}, [state, name, context, onChange]);
if (autoHide && !isControlPresented(name, state)) {
if (autoHide && !isControlPresented(name, state, defaultValue)) {
return null;
}
let value: any = valueControlled;
let value: any = valueControlled ?? defaultValue ?? undefined;
if (state) {
if (name in state) {
value = state[name];
} else if (rest.defaultValue !== undefined) {
value = rest.defaultValue;
}
if (state && name !== undefined && name in state) {
value = state[name];
}
if (mapState) {
@@ -6,9 +6,14 @@
* you may not use this file except in compliance with the License.
*/
export function isControlPresented(name: string | undefined, state: any): boolean {
export function isControlPresented(
name: string | undefined, state: any, defaultValue?: string | number | boolean
): boolean {
if (state !== undefined && name !== undefined) {
return name in state && state[name] !== null;
if (name in state) {
return state[name] !== null;
}
return defaultValue !== undefined;
}
return true;
}
@@ -16,8 +16,8 @@ import { TextPlaceholder } from '../../TextPlaceholder';
import { RenderField } from './RenderField';
interface ObjectPropertyFormProps extends ILayoutSizeProps {
properties: ObjectPropertyInfo[] | undefined;
state: Record<string, string | number>;
properties: ObjectPropertyInfo[];
state?: Record<string, string | number>;
category?: string | null;
editable?: boolean;
autofillToken?: string;
@@ -48,7 +48,7 @@ export const ObjectPropertyInfoFormNew: React.FC<ObjectPropertyFormProps> = obse
}
}, [onFocus]);
if (!properties || properties.length === 0) {
if (properties.length === 0) {
return <TextPlaceholder>Properties empty</TextPlaceholder>;
}
@@ -21,7 +21,7 @@ const RESERVED_KEYWORDS = ['no', 'off', 'new-password'];
interface RenderFieldProps {
property: ObjectPropertyInfo;
state: Record<string, any>;
state?: Record<string, any>;
editable?: boolean;
autofillToken?: string;
disabled?: boolean;
@@ -32,20 +32,31 @@ interface RenderFieldProps {
className?: string;
}
function isCheckbox(property: ObjectPropertyInfo) {
return property.dataType?.toLowerCase() === 'boolean';
type ControlType = 'checkbox' | 'combobox' | 'link' | 'input';
function getControlTypeFor(property: ObjectPropertyInfo): ControlType {
const dataType = property.dataType?.toLowerCase();
if (dataType === 'boolean') {
return 'checkbox';
} else if (property.validValues && property.validValues.length > 0) {
return 'combobox';
} else if (property.features.includes('href')) {
return 'link';
}
return 'input';
}
function getDefaultValueFor(property: ObjectPropertyInfo) {
const checkbox = isCheckbox(property);
const value = property.value;
function getValue(value: any, controlType: ControlType) {
const checkbox = controlType === 'checkbox';
if (value === null || value === undefined) {
return checkbox ? false : '';
}
if (typeof value === 'string') {
return checkbox ? value === 'true' : value;
return checkbox ? value.toLowerCase() === 'true' : value;
}
return value.displayName || value.value || JSON.stringify(value);
@@ -63,17 +74,17 @@ export const RenderField: React.FC<RenderFieldProps> = observer(function RenderF
onFocus,
className,
}) {
const href = property.features.includes('href');
const controltype = getControlTypeFor(property);
const password = property.features.includes('password');
const checkbox = isCheckbox(property);
const combobox = property.validValues && property.validValues.length > 0;
const defaultValue = getDefaultValueFor(property);
const value = getValue(property.value, controltype);
const defaultValue = getValue(property.defaultValue, controltype);
let description: string | undefined;
if (href) {
if (controltype === 'link') {
return (
<FormFieldDescriptionNew label={property.displayName} className={className}>
<Link href={state[property.id!]} target='_blank' rel='noopener noreferrer'>{property.description}</Link>
<Link href={state?.[property.id!]} target='_blank' rel='noopener noreferrer'>{property.description}</Link>
</FormFieldDescriptionNew>
);
}
@@ -84,7 +95,7 @@ export const RenderField: React.FC<RenderFieldProps> = observer(function RenderF
}
return (
<FormFieldDescriptionNew title={property.description} label={property.displayName} className={className}>
{state[property.id!]}
{state?.[property.id!]}
</FormFieldDescriptionNew>
);
}
@@ -93,12 +104,12 @@ export const RenderField: React.FC<RenderFieldProps> = observer(function RenderF
description = 'Password saved';
}
if (checkbox) {
if (controltype === 'checkbox') {
return (
<FieldCheckboxNew
name={property.id!}
state={state}
defaultChecked={defaultValue}
checked={state === undefined ? value : undefined}
title={property.description}
disabled={disabled || readOnly}
className={className}
@@ -108,7 +119,7 @@ export const RenderField: React.FC<RenderFieldProps> = observer(function RenderF
);
}
if (combobox) {
if (controltype === 'combobox') {
return (
<ComboboxNew
name={property.id!}
@@ -116,7 +127,7 @@ export const RenderField: React.FC<RenderFieldProps> = observer(function RenderF
items={property.validValues!}
keySelector={value => value}
valueSelector={value => value}
defaultValue={property.defaultValue}
defaultValue={defaultValue}
title={property.description}
disabled={disabled}
className={className}
@@ -132,7 +143,7 @@ export const RenderField: React.FC<RenderFieldProps> = observer(function RenderF
title={property.description}
name={property.id!}
state={state}
defaultValue={defaultValue}
value={state === undefined ? value : undefined}
description={description}
disabled={disabled}
readOnly={readOnly}
@@ -227,7 +227,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
</Group>
</Container>
<Container medium gap>
{(authModel && !driver.data?.anonymousAccess) && (
{(authModel && !driver.data?.anonymousAccess && properties) && (
<Group form gap>
<GroupTitle>{translate('connections_connection_edit_authentication')}</GroupTitle>
<Container wrap gap>
@@ -30,12 +30,13 @@ export const ObjectProperties = observer(function ObjectProperties({
const { dbObject, isLoading } = useDatabaseObjectInfo(objectId);
const styles = useStyles(BASE_CONTAINERS_STYLES);
const { categories, isUncategorizedExists } = useObjectPropertyCategories(dbObject?.properties ?? emptyArray);
const properties = dbObject?.properties;
if (!dbObject?.properties && isLoading) {
if (!properties && isLoading) {
return <Loader />;
}
if (!dbObject?.properties || dbObject.properties.length === 0) {
if (!properties || properties.length === 0) {
return <TextPlaceholder>{translate('plugin_object_viewer_table_no_items')}</TextPlaceholder>;
}
@@ -44,9 +45,8 @@ export const ObjectProperties = observer(function ObjectProperties({
{isUncategorizedExists && (
<Group gap large>
<ObjectPropertyInfoFormNew
properties={dbObject?.properties}
properties={properties}
category={null}
state={{}}
small
readOnly
/>
@@ -56,9 +56,8 @@ export const ObjectProperties = observer(function ObjectProperties({
<Group key={category} gap large>
<GroupTitle>{category}</GroupTitle>
<ObjectPropertyInfoFormNew
properties={dbObject?.properties}
properties={properties}
category={category}
state={{}}
small
readOnly
/>