Skip to content

Commit

Permalink
Minor: OSS requirements for the custom property automation action (#1…
Browse files Browse the repository at this point in the history
…9507)

* Fix the return type for the custom property API function

* Break down the EditTableTypePropertyModal.tsx component

* Fix the custom property inputs for date-time type properties

* auto lint formatting changes

* Add the autoFocus prop for DataAssetAsyncSelectList

* Fix and add the unit tests for new code

(cherry picked from commit 638e988)
  • Loading branch information
aniketkatkar97 authored and OpenMetadata Release Bot committed Feb 8, 2025
1 parent 9a6480b commit 1dc8da2
Show file tree
Hide file tree
Showing 17 changed files with 609 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface FetchOptionsResponse {

export interface DataAssetAsyncSelectListProps {
mode?: 'multiple';
autoFocus?: boolean;
id?: string;
className?: string;
placeholder?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {

const DataAssetAsyncSelectList: FC<DataAssetAsyncSelectListProps> = ({
mode,
autoFocus = true,
onChange,
debounceTimeout = 800,
initialOptions,
Expand Down Expand Up @@ -242,8 +243,8 @@ const DataAssetAsyncSelectList: FC<DataAssetAsyncSelectListProps> = ({
return (
<Select
allowClear
autoFocus
showSearch
autoFocus={autoFocus}
data-testid="asset-select-list"
dropdownRender={dropdownRender}
filterOption={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,24 +224,16 @@ export const AdvanceSearchProvider = ({

Object.entries(res).forEach(([_, fields]) => {
if (Array.isArray(fields) && fields.length > 0) {
fields.forEach(
(field: {
name: string;
type: string;
customPropertyConfig: {
config: string | string[];
fields.forEach((field) => {
if (field.name && field.type) {
const { subfieldsKey, dataObject } =
advancedSearchClassBase.getCustomPropertiesSubFields(field);
subfields[subfieldsKey] = {
...dataObject,
valueSources: dataObject.valueSources as ValueSource[],
};
}) => {
if (field.name && field.type) {
const { subfieldsKey, dataObject } =
advancedSearchClassBase.getCustomPropertiesSubFields(field);
subfields[subfieldsKey] = {
...dataObject,
valueSources: dataObject.valueSources as ValueSource[],
};
}
}
);
});
}
});
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import {
noop,
omitBy,
toNumber,
toUpper,
} from 'lodash';
import moment, { Moment } from 'moment';
import React, {
Expand Down Expand Up @@ -65,6 +64,7 @@ import { CSMode } from '../../../enums/codemirror.enum';
import { SearchIndex } from '../../../enums/search.enum';
import { EntityReference } from '../../../generated/entity/type';
import { Config } from '../../../generated/type/customProperty';
import { getCustomPropertyMomentFormat } from '../../../utils/CustomProperty.utils';
import { calculateInterval } from '../../../utils/date-time/DateTimeUtils';
import entityUtilClassBase from '../../../utils/EntityUtilClassBase';
import { getEntityName } from '../../../utils/EntityUtils';
Expand Down Expand Up @@ -278,9 +278,9 @@ export const PropertyValue: FC<PropertyValueProps> = ({

case 'date-cp':
case 'dateTime-cp': {
// Default format is 'yyyy-mm-dd'
const format = toUpper(
(property.customPropertyConfig?.config as string) ?? 'yyyy-mm-dd'
const format = getCustomPropertyMomentFormat(
propertyType.name,
property.customPropertyConfig?.config
);

const initialValues = {
Expand Down Expand Up @@ -327,8 +327,11 @@ export const PropertyValue: FC<PropertyValueProps> = ({
}

case 'time-cp': {
const format =
(property.customPropertyConfig?.config as string) ?? 'HH:mm:ss';
const format = getCustomPropertyMomentFormat(
propertyType.name,
property.customPropertyConfig?.config
);

const initialValues = {
time: value ? moment(value, format) : undefined,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2025 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CustomProperty } from '../../../../generated/entity/type';
import { TableTypePropertyValueType } from '../CustomPropertyTable.interface';

export interface EditTableTypePropertyModalProps {
isVisible: boolean;
isUpdating: boolean;
property: CustomProperty;
columns: string[];
rows: Record<string, string>[];
onCancel: () => void;
onSave: (data: TableTypePropertyValueType) => Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2025 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import '@testing-library/jest-dom/extend-expect';
import { fireEvent, render, waitFor } from '@testing-library/react';
import React from 'react';
import { CustomProperty } from '../../../../generated/type/customProperty';
import EditTableTypePropertyModal from './EditTableTypePropertyModal';
import { EditTableTypePropertyModalProps } from './EditTableTypePropertyModal.interface';

jest.mock('./TableTypePropertyEditTable', () =>
jest.fn().mockImplementation(() => <div>TableTypePropertyEditTable</div>)
);

jest.mock('./TableTypePropertyView', () =>
jest.fn().mockImplementation(() => <div>TableTypePropertyView</div>)
);

const mockOnCancel = jest.fn();
const mockOnSave = jest.fn().mockResolvedValue(undefined);

const defaultProps: EditTableTypePropertyModalProps = {
isVisible: true,
isUpdating: false,
property: {} as CustomProperty,
columns: ['column1', 'column2'],
rows: [{ column1: 'value1', column2: 'value2' }],
onCancel: mockOnCancel,
onSave: mockOnSave,
};

describe('EditTableTypePropertyModal', () => {
it('should render the modal with the correct title', () => {
const { getByText } = render(
<EditTableTypePropertyModal {...defaultProps} />
);

expect(getByText('label.edit-entity-name')).toBeInTheDocument();
});

it('should call onCancel when the cancel button is clicked', () => {
const { getByTestId } = render(
<EditTableTypePropertyModal {...defaultProps} />
);
fireEvent.click(getByTestId('cancel-update-table-type-property'));

expect(mockOnCancel).toHaveBeenCalled();
});

it('should call onSave with the correct data when the update button is clicked', async () => {
const { getByTestId } = render(
<EditTableTypePropertyModal {...defaultProps} />
);
fireEvent.click(getByTestId('update-table-type-property'));
await waitFor(() =>
expect(mockOnSave).toHaveBeenCalledWith({
rows: [{ column1: 'value1', column2: 'value2' }],
columns: ['column1', 'column2'],
})
);
});

it('should add a new row when the add new row button is clicked', () => {
const { getByTestId, getByText } = render(
<EditTableTypePropertyModal {...defaultProps} />
);
fireEvent.click(getByTestId('add-new-row'));

expect(getByText('TableTypePropertyEditTable')).toBeInTheDocument();
});

it('should render TableTypePropertyView when dataSource is empty', () => {
const props = { ...defaultProps, rows: [] };
const { getByText } = render(<EditTableTypePropertyModal {...props} />);

expect(getByText('TableTypePropertyView')).toBeInTheDocument();
});

it('should render TableTypePropertyEditTable when dataSource is not empty', () => {
const { getByText } = render(
<EditTableTypePropertyModal {...defaultProps} />
);

expect(getByText('TableTypePropertyEditTable')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ReactDataGrid from '@inovua/reactdatagrid-community';
import '@inovua/reactdatagrid-community/index.css';
import { TypeComputedProps } from '@inovua/reactdatagrid-community/types';
import { Button, Modal, Typography } from 'antd';
import { isEmpty, omit } from 'lodash';
import React, { FC, MutableRefObject, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { CustomProperty } from '../../../../generated/type/customProperty';
import { getEntityName } from '../../../../utils/EntityUtils';
import { TableTypePropertyValueType } from '../CustomPropertyTable.interface';
import './edit-table-type-property.less';
import { EditTableTypePropertyModalProps } from './EditTableTypePropertyModal.interface';
import TableTypePropertyEditTable from './TableTypePropertyEditTable';
import TableTypePropertyView from './TableTypePropertyView';

interface EditTableTypePropertyModalProps {
isVisible: boolean;
isUpdating: boolean;
property: CustomProperty;
columns: string[];
rows: Record<string, string>[];
onCancel: () => void;
onSave: (data: TableTypePropertyValueType) => Promise<void>;
}

let inEdit = false;

const EditTableTypePropertyModal: FC<EditTableTypePropertyModalProps> = ({
isVisible,
isUpdating,
Expand All @@ -54,91 +42,16 @@ const EditTableTypePropertyModal: FC<EditTableTypePropertyModalProps> = ({
MutableRefObject<TypeComputedProps | null>
>({ current: null });

const filterColumns = columns.map((column) => ({
name: column,
header: column,
defaultFlex: 1,
sortable: false,
minWidth: 180,
}));

const onEditComplete = useCallback(
({ value, columnId, rowId }) => {
const data = [...dataSource];

data[rowId][columnId] = value;

setDataSource(data);
const handleEditGridRef = useCallback(
(ref: MutableRefObject<TypeComputedProps | null>) => {
setGridRef(ref);
},
[dataSource]
[]
);

const onEditStart = () => {
inEdit = true;
};

const onEditStop = () => {
requestAnimationFrame(() => {
inEdit = false;
gridRef.current?.focus();
});
};

const onKeyDown = (event: KeyboardEvent) => {
if (inEdit) {
if (event.key === 'Escape') {
const [rowIndex, colIndex] = gridRef.current?.computedActiveCell ?? [
0, 0,
];
const column = gridRef.current?.getColumnBy(colIndex);

gridRef.current?.cancelEdit?.({
rowIndex,
columnId: column?.name ?? '',
});
}

return;
}
const grid = gridRef.current;
if (!grid) {
return;
}
let [rowIndex, colIndex] = grid.computedActiveCell ?? [0, 0];

if (event.key === ' ' || event.key === 'Enter') {
const column = grid.getColumnBy(colIndex);
grid.startEdit?.({ columnId: column.name ?? '', rowIndex });
event.preventDefault();

return;
}
if (event.key !== 'Tab') {
return;
}
event.preventDefault();
event.stopPropagation();

const direction = event.shiftKey ? -1 : 1;

const columns = grid.visibleColumns;
const rowCount = grid.count;

colIndex += direction;
if (colIndex === -1) {
colIndex = columns.length - 1;
rowIndex -= 1;
}
if (colIndex === columns.length) {
rowIndex += 1;
colIndex = 0;
}
if (rowIndex < 0 || rowIndex === rowCount) {
return;
}

grid?.setActiveCell([rowIndex, colIndex]);
};
const handleEditDataSource = useCallback((data: Record<string, string>[]) => {
setDataSource(data);
}, []);

const handleAddRow = useCallback(() => {
setDataSource((data) => {
Expand Down Expand Up @@ -207,20 +120,12 @@ const EditTableTypePropertyModal: FC<EditTableTypePropertyModalProps> = ({
{isEmpty(dataSource) ? (
<TableTypePropertyView columns={columns} rows={rows} />
) : (
<ReactDataGrid
editable
className="edit-table-type-property"
columns={filterColumns}
<TableTypePropertyEditTable
columns={columns}
dataSource={dataSource}
handle={setGridRef}
idProperty="id"
minRowHeight={30}
showZebraRows={false}
style={{ height: '350px' }}
onEditComplete={onEditComplete}
onEditStart={onEditStart}
onEditStop={onEditStop}
onKeyDown={onKeyDown}
gridRef={gridRef}
handleEditDataSource={handleEditDataSource}
handleEditGridRef={handleEditGridRef}
/>
)}
</Modal>
Expand Down
Loading

0 comments on commit 1dc8da2

Please sign in to comment.