diff --git a/src/platform/packages/private/kbn-index-editor/src/components/data_grid.tsx b/src/platform/packages/private/kbn-index-editor/src/components/data_grid.tsx index 79cadb7f7050..a7fe523d3c6b 100644 --- a/src/platform/packages/private/kbn-index-editor/src/components/data_grid.tsx +++ b/src/platform/packages/private/kbn-index-editor/src/components/data_grid.tsx @@ -113,8 +113,15 @@ const DataGrid: React.FC = (props) => { ); const CellValueRenderer = useMemo(() => { - return getCellValueRenderer(rows, editingCell, savingDocs, setEditingCell, onValueChange); - }, [rows, editingCell, setEditingCell, onValueChange, savingDocs]); + return getCellValueRenderer( + rows, + props.columns, + editingCell, + savingDocs, + setEditingCell, + onValueChange + ); + }, [rows, props.columns, editingCell, setEditingCell, onValueChange, savingDocs]); const externalCustomRenderers: CustomCellRenderer = useMemo(() => { return activeColumns.reduce((acc, columnId) => { diff --git a/src/platform/packages/private/kbn-index-editor/src/components/row_column_creator.tsx b/src/platform/packages/private/kbn-index-editor/src/components/row_column_creator.tsx index d957eff0a58b..324cd660c0d3 100644 --- a/src/platform/packages/private/kbn-index-editor/src/components/row_column_creator.tsx +++ b/src/platform/packages/private/kbn-index-editor/src/components/row_column_creator.tsx @@ -69,7 +69,7 @@ export const RowColumnCreator = ({ columns }: { columns: DatatableColumn[] }) => onChange={updateRow(column.id)} autoFocus={index === 0} css={css` - min-width: ${230}px; + min-width: ${180}px; `} /> diff --git a/src/platform/packages/private/kbn-index-editor/src/components/value_input.tsx b/src/platform/packages/private/kbn-index-editor/src/components/value_input.tsx index 0bb924744c07..dedaf2bf6ae5 100644 --- a/src/platform/packages/private/kbn-index-editor/src/components/value_input.tsx +++ b/src/platform/packages/private/kbn-index-editor/src/components/value_input.tsx @@ -7,13 +7,17 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { EuiFieldText } from '@elastic/eui'; -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; +import type { DatatableColumn } from '@kbn/expressions-plugin/common'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { getInputComponentForType } from './value_inputs_factory'; +import { KibanaContextExtra } from '../types'; interface ValueInputProps { value?: string; columnName?: string; + columns?: DatatableColumn[]; onBlur?: (event: React.FocusEvent) => void; onEnter?: (value: string) => void; onChange?: (value: string) => void; @@ -24,33 +28,55 @@ interface ValueInputProps { export const ValueInput = ({ value = '', columnName = '', + columns, onBlur, onEnter, onChange, autoFocus = false, className = '', }: ValueInputProps) => { + const { + services: { notifications }, + } = useKibana(); const [editValue, setEditValue] = useState(value); + const [error, setError] = useState(null); + + const columnType = useMemo(() => { + if (!columns || !columnName) return; + const col = columns.find((c) => c.name === columnName); + return col?.meta?.type; + }, [columns, columnName]); const onKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { - // Perform Validations + if (error) { + notifications.toasts.addDanger({ + title: error, + }); + return; + } + onEnter?.(editValue); } }; const onBlurHandler = (event: React.FocusEvent) => { - if (onBlur) { - onBlur(event); + if (error) { + notifications.toasts.addDanger({ + title: error, + }); + return; } - // Perform Validations ? + onBlur?.(event); }; + const InputComponent = useMemo(() => getInputComponentForType(columnType), [columnType]); + return ( - void; export const getCellValueRenderer = ( rows: DataTableRecord[], + columns: DatatableColumn[], editingCell: { row: number | null; col: string | null }, savingDocs: PendingSave | undefined, onEditStart: (update: { row: number | null; col: string | null }) => void, @@ -40,9 +42,8 @@ export const getCellValueRenderer = cellValue = pendingSaveValue; } else if (row.flattened) { // Otherwise, use the value from the row - cellValue = row.flattened[columnId]; + cellValue = row.flattened[columnId]?.toString(); } - if (cellValue == null) { return null; } @@ -60,6 +61,7 @@ export const getCellValueRenderer = onValueChange(docId!, { [columnId]: value }); }} columnName={columnId} + columns={columns} value={cellValue} autoFocus /> diff --git a/src/platform/packages/private/kbn-index-editor/src/components/value_inputs_factory.tsx b/src/platform/packages/private/kbn-index-editor/src/components/value_inputs_factory.tsx new file mode 100644 index 000000000000..48cfed6f7574 --- /dev/null +++ b/src/platform/packages/private/kbn-index-editor/src/components/value_inputs_factory.tsx @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { useCallback } from 'react'; +import { EuiFieldText, EuiFieldNumber } from '@elastic/eui'; +import { DatatableColumnType } from '@kbn/expressions-plugin/common'; +import { i18n } from '@kbn/i18n'; + +export interface ValueInputProps { + onError?: (error: string | null) => void; + value: string; + label?: string; + onChange: (event: React.ChangeEvent) => void; + onBlur?: (event: React.FocusEvent) => void; + onKeyDown?: (event: React.KeyboardEvent) => void; + autoFocus?: boolean; + className?: string; + isInvalid?: boolean; + placeholder?: string; +} + +export const StringInput: React.FC = ({ onError, ...restOfProps }) => ( + +); + +export const NumberInput: React.FC = ({ onError, ...restOfProps }) => ( + +); + +export const BooleanInput: React.FC = ({ onError, onChange, ...restOfProps }) => { + const [error, setError] = React.useState(null); + + const onChangeHandler = useCallback( + (e: React.ChangeEvent) => { + if (!['true', 'false'].includes(e.target.value.toLowerCase())) { + const booleanError = i18n.translate('indexEditor.cellValueInput.validation.boolean', { + defaultMessage: 'Value must be true or false', + }); + setError(booleanError); + onError?.(booleanError); + } else { + setError(null); + onError?.(null); + } + onChange?.(e); + }, + [onError, onChange] + ); + + return ( + + ); +}; + +export function getInputComponentForType( + type: DatatableColumnType | undefined +): React.FC { + switch (type) { + case 'number': + return NumberInput; + case 'boolean': + return BooleanInput; + default: + return StringInput; + } +}