From 9c8492c7a3dc49f60917e2bfd1061f3a949b1ddb Mon Sep 17 00:00:00 2001 From: Aniket Katkar <51777795+aniketkatkar97@users.noreply.github.com> Date: Wed, 30 Nov 2022 20:15:26 +0530 Subject: [PATCH] Fix(UI) #8555: Tags removal issue (#9055) * Fixed Error thrown while removing tags from data asset details page * Fixed bug where user were not able to remove the tags on Dashboard Details page * Changes in MlModelFeaturesList component: - Tag removal bug fix - Removed tailwind use and replaced with antd - Code optimisation - Localisation changes * Moved height and width styling from app.less to size.less * Moved tags sorting functionality to a common util function * Removed redundant labels in en-us and replaced with common reusable label * Removed unnecessary code * localisation changes * Added unit tests for added functions in CommonUtils and DashboardDetailsUtils * Added missing localisation changes for MlModelFeaturesList --- .../DashboardDetails.component.tsx | 74 +-- .../CustomControls.component.tsx | 2 +- .../EntityTable/EntityTable.component.tsx | 6 +- .../MlModelDetail/MlModelFeaturesList.tsx | 436 +++++++++--------- .../MlModelDetail/SourceList.component.tsx | 89 ++++ .../MlModelDetail/SourceList.style.less | 17 + .../PipelineDetails.component.tsx | 8 +- .../components/TeamDetails/TeamDetailsV1.tsx | 4 +- .../VersionTable/VersionTable.component.tsx | 2 +- .../ui/src/components/nav-bar/NavBar.tsx | 2 +- .../ui/src/locale/languages/en-us.json | 22 +- .../DashboardDetailsPage.component.tsx | 6 +- .../ui/src/pages/KPIPage/EditKPIPage.tsx | 2 +- .../resources/ui/src/pages/service/index.tsx | 16 +- .../src/main/resources/ui/src/styles/app.less | 143 +----- .../ui/src/styles/components/size.less | 110 ++++- .../ui/src/utils/CommonUtils.mock.ts | 107 +++++ .../ui/src/utils/CommonUtils.test.ts | 38 ++ .../resources/ui/src/utils/CommonUtils.tsx | 6 + .../src/utils/DashboardDetailsUtils.mock.ts | 188 ++++++++ .../src/utils/DashboardDetailsUtils.test.ts | 36 ++ .../ui/src/utils/DashboardDetailsUtils.ts | 9 + .../resources/ui/src/utils/TableUtils.tsx | 6 +- 23 files changed, 920 insertions(+), 409 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/SourceList.component.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/SourceList.style.less create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.mock.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.test.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.mock.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.test.ts diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx index c19f4aeb157f..ac85af964b2d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx @@ -586,7 +586,7 @@ const DashboardDetails = ({ - + {feature.tags?.length ? ( + + + + ) : ( + + + + )} + + + + + + + + + {t('label.description')}: + + + {feature.description ? ( + ) : ( - - - + + {t('label.no-description')} + )} - - - - - -
- Description:{' '} -
- {feature.description ? ( - - ) : ( - No description - )} - - - -
-
- - - ))} - - {!isEmpty(selectedFeature) && editDescription && ( - - )} - - ); - } else { - return ( -
- -

No features data available

-
-
- ); - } - }; - - return render(); + + + +
+
+ + + + + + + + ))} + + {!isEmpty(selectedFeature) && editDescription && ( + + )} + + ); + } else { + return ( + + {t('message.no-features-data-available')} + + ); + } }; export default MlModelFeaturesList; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/SourceList.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/SourceList.component.tsx new file mode 100644 index 000000000000..a21492b72cd9 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/SourceList.component.tsx @@ -0,0 +1,89 @@ +/* + * Copyright 2022 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 { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Col, Row, Space, Typography } from 'antd'; +import React, { useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; +import { EntityType } from '../../enums/entity.enum'; +import { MlFeature } from '../../generated/entity/data/mlmodel'; +import { getEntityLink } from '../../utils/TableUtils'; +import './SourceList.style.less'; + +const SourceList = ({ feature }: { feature: MlFeature }) => { + const { t } = useTranslation(); + const [isActive, setIsActive] = useState(false); + const showFeatureSources = useMemo( + () => feature.featureSources && feature.featureSources.length && isActive, + [feature, isActive] + ); + + return ( +
+ + setIsActive((prev) => !prev)}> + + + + {t('label.source-plural')} + + + {showFeatureSources && + feature.featureSources?.map((source, i) => ( + + {String(i + 1).padStart(2, '0')} + + + {t('label.name')}: + + + {source.name} + + + + + {t('label.type')}: + + + {source.dataType} + + + + + {t('label.data-source')}: + + + {source.dataSource?.fullyQualifiedName} + + + + ))} +
+ ); +}; + +export default SourceList; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/SourceList.style.less b/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/SourceList.style.less new file mode 100644 index 000000000000..f4fe39dbdd73 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/SourceList.style.less @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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. + */ + +.feature-source-info { + border-top: 1px solid rgba(220, 227, 236); + padding-top: 16px; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx index 09731ff56a28..06fc5de9f8c5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx @@ -623,7 +623,7 @@ const PipelineDetails = ({ {allowTestConn && isAirflowRunning && ( diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/app.less b/openmetadata-ui/src/main/resources/ui/src/styles/app.less index 7f101769676d..725cec04d6f1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/app.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/app.less @@ -84,121 +84,33 @@ text-decoration: underline; } -// Radius - -.rounded-4 { - border-radius: 4px; -} -.rounded-full { - border-radius: 9999px; -} - -// Width -.w-4 { - width: 16px; -} -.w-5 { - width: 20px; -} -.w-8 { - width: 32px; -} -.w-9 { - width: 36px; -} -.w-16 { - width: 64px; -} -.w-24 { - width: 6rem; -} -.w-32 { - width: 8rem; -} -.w-36 { - width: 9rem; -} -.w-72 { - width: 288px; -} -.w-48 { - width: 12rem; -} -.w-11-12 { - width: 91.666667%; +// Border +.no-border { + border: none; } -.w-min-0 { - min-width: 0; -} -.w-max-20 { - max-width: 20%; -} -.w-max-200 { - max-width: 200px; -} -.w-max-500 { - max-width: 500px; -} -.w-max-1080 { - max-width: 1080px; -} -.w-500 { - width: 500px; -} -.w-300 { - width: 300px; -} -.w-60 { - width: 60%; +.border-1 { + border-width: 1px; } -.w-3\.5 { - width: 0.875rem; +.border-l-2 { + border-left: 2px; } -.w-full { - width: 100%; +.border-main { + border-color: @border-color; } -.w-max-256 { - max-width: 256px; +.border-primary { + border-color: @primary; } -.w-auto { - width: auto; +.border-gray { + border-color: @gray; } -//Height -.h-4 { - height: 16px; -} -.h-7 { - height: 28px; -} -.h-8 { - height: 32px; -} -.h-9 { - height: 36px; -} -.h-100 { - height: 400px; -} -.h-min-100 { - min-height: 100vh; -} -.h-min-80 { - min-height: 80vh; -} -.h-min-50 { - min-height: 50vh; -} -.h-3\.5 { - height: 0.875rem; -} +// Radius -.h-full { - height: 100%; +.rounded-4 { + border-radius: 4px; } - -.h-min-full { - min-height: 100%; +.rounded-full { + border-radius: 9999px; } // Text alignment @@ -260,25 +172,6 @@ .text-semi-bold { font-weight: 500; } - -.border-1 { - border-width: 1px; -} - -.border-l-2 { - border-left: 2px; -} - -.border-primary { - border-color: @primary; -} -.border-gray { - border-color: @gray; -} -.border-main { - border-color: @border-color; -} - .bg-body-main { background: @body-bg-color; } diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/components/size.less b/openmetadata-ui/src/main/resources/ui/src/styles/components/size.less index 7c83d5d595ff..1dca2eb1b1c5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/components/size.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/components/size.less @@ -11,9 +11,115 @@ * limitations under the License. */ +.w-4 { + width: 1rem /* 16px */; +} +.w-5 { + width: 20px; +} +.w-8 { + width: 32px; +} +.w-9 { + width: 36px; +} +.w-16 { + width: 64px; +} +.w-24 { + width: 6rem; +} +.w-32 { + width: 8rem; +} +.w-36 { + width: 9rem; +} .w-40 { width: 10rem /* 160px */; } -.w-4 { - width: 1rem /* 16px */; +.w-72 { + width: 288px; +} +.w-48 { + width: 12rem; +} +.w-11-12 { + width: 91.666667%; +} +.w-min-0 { + min-width: 0; +} +.w-max-20 { + max-width: 20%; +} +.w-max-200 { + max-width: 200px; +} +.w-max-500 { + max-width: 500px; +} +.w-max-1080 { + max-width: 1080px; +} +.w-500 { + width: 500px; +} +.w-300 { + width: 300px; +} +.w-60 { + width: 60%; +} +.w-3\.5 { + width: 0.875rem; +} +.w-full { + width: 100%; +} +.w-max-256 { + max-width: 256px; +} +.w-auto { + width: auto; +} + +//Height +.h-4 { + height: 16px; +} +.h-7 { + height: 28px; +} +.h-8 { + height: 32px; +} +.h-9 { + height: 36px; +} +.h-100 { + height: 400px; +} +.h-min-100 { + min-height: 100vh; +} +.h-min-80 { + min-height: 80vh; +} +.h-min-50 { + min-height: 50vh; +} +.h-3\.5 { + height: 0.875rem; +} + +.h-full { + height: 100%; +} +.h-auto { + height: auto; +} + +.h-min-full { + min-height: 100%; } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.mock.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.mock.ts new file mode 100644 index 000000000000..9006a1592e2d --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.mock.ts @@ -0,0 +1,107 @@ +/* + * Copyright 2022 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 { LabelType, State, TagSource } from '../generated/entity/data/chart'; + +export const mockTags = [ + { + tagFQN: 'PII.NonSensitive', + description: + 'PII which is easily accessible from public sources and can include zip code, race, gender, and date of birth.', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'PersonalData.Personal', + description: + 'Data that can be used to directly or indirectly identify a person.', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'ab.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'persona.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'aa.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'ac.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, +]; + +export const sortedMockTags = [ + { + tagFQN: 'aa.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'ab.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'ac.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'persona.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'PersonalData.Personal', + description: + 'Data that can be used to directly or indirectly identify a person.', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'PII.NonSensitive', + description: + 'PII which is easily accessible from public sources and can include zip code, race, gender, and date of birth.', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, +]; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.test.ts new file mode 100644 index 000000000000..6d74c9d03796 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.test.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2022 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 { cloneDeep } from 'lodash'; +import { sortTagsCaseInsensitive } from './CommonUtils'; +import { mockTags, sortedMockTags } from './CommonUtils.mock'; + +describe('Tests for CommonUtils', () => { + describe('Tests for sortTagsCaseInsensitive function', () => { + it('Input of unsorted array to sortTagsCaseInsensitive should return array of tags sorted by tagFQN', () => { + expect(sortTagsCaseInsensitive(cloneDeep(mockTags))).toEqual( + sortedMockTags + ); + }); + + it('Input of sorted array to sortTagsCaseInsensitive should return array of tags sorted by tagFQN', () => { + expect(sortTagsCaseInsensitive(cloneDeep(sortedMockTags))).toEqual( + sortedMockTags + ); + }); + + it('Array returned by sortTagsCaseInsensitive should not be equal to the unsorted input array of tags', () => { + expect(sortTagsCaseInsensitive(cloneDeep(mockTags))).not.toEqual( + mockTags + ); + }); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx index 0edf7b8387dd..fd7fa39b4948 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx @@ -1055,3 +1055,9 @@ export const getTrimmedContent = (content: string, limit: number) => { return refinedContent.join(' '); }; + +export const sortTagsCaseInsensitive = (tags: TagLabel[]) => { + return tags.sort((tag1, tag2) => + tag1.tagFQN.toLowerCase() < tag2.tagFQN.toLowerCase() ? -1 : 1 + ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.mock.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.mock.ts new file mode 100644 index 000000000000..cf50aeac1577 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.mock.ts @@ -0,0 +1,188 @@ +/* + * Copyright 2022 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 { + ChartType, + LabelType, + State, + TagSource, +} from '../generated/entity/data/chart'; +import { DashboardServiceType } from '../generated/entity/data/dashboard'; + +export const mockCharts = [ + { + id: '86dad1d4-8830-4d35-ab03-dd6190c1f05d', + name: '127', + displayName: 'Are you an ethnic minority in your city?', + fullyQualifiedName: 'sample_superset.127', + description: '', + version: 3.5, + updatedAt: 1669804896748, + updatedBy: 'admin', + chartType: ChartType.Other, + chartUrl: + 'http://localhost:8088/superset/explore/?form_data=%7B%22slice_id%22%3A%20127%7D', + href: 'http://localhost:8585/api/v1/charts/86dad1d4-8830-4d35-ab03-dd6190c1f05d', + tags: [ + { + tagFQN: 'PII.NonSensitive', + description: + 'PII which is easily accessible from public sources and can include zip code, race, gender, and date of birth.', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'PersonalData.Personal', + description: + 'Data that can be used to directly or indirectly identify a person.', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'ab.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'persona.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'aa.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'ac.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + ], + service: { + id: '1c7d6a14-69fe-4ee6-be3c-e6ff01c04055', + type: 'dashboardService', + name: 'sample_superset', + fullyQualifiedName: 'sample_superset', + deleted: false, + href: 'http://localhost:8585/api/v1/services/dashboardServices/1c7d6a14-69fe-4ee6-be3c-e6ff01c04055', + }, + serviceType: DashboardServiceType.Superset, + changeDescription: { + fieldsAdded: [ + { + name: 'tags', + newValue: '', + }, + ], + fieldsUpdated: [], + fieldsDeleted: [], + previousVersion: 3.4, + }, + deleted: false, + }, +]; + +export const sortedTagsMockCharts = [ + { + id: '86dad1d4-8830-4d35-ab03-dd6190c1f05d', + name: '127', + displayName: 'Are you an ethnic minority in your city?', + fullyQualifiedName: 'sample_superset.127', + description: '', + version: 3.5, + updatedAt: 1669804896748, + updatedBy: 'admin', + chartType: ChartType.Other, + chartUrl: + 'http://localhost:8088/superset/explore/?form_data=%7B%22slice_id%22%3A%20127%7D', + href: 'http://localhost:8585/api/v1/charts/86dad1d4-8830-4d35-ab03-dd6190c1f05d', + tags: [ + { + tagFQN: 'aa.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'ab.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'ac.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'persona.tag', + description: '', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'PersonalData.Personal', + description: + 'Data that can be used to directly or indirectly identify a person.', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + { + tagFQN: 'PII.NonSensitive', + description: + 'PII which is easily accessible from public sources and can include zip code, race, gender, and date of birth.', + source: TagSource.Tag, + labelType: LabelType.Manual, + state: State.Confirmed, + }, + ], + service: { + id: '1c7d6a14-69fe-4ee6-be3c-e6ff01c04055', + type: 'dashboardService', + name: 'sample_superset', + fullyQualifiedName: 'sample_superset', + deleted: false, + href: 'http://localhost:8585/api/v1/services/dashboardServices/1c7d6a14-69fe-4ee6-be3c-e6ff01c04055', + }, + serviceType: DashboardServiceType.Superset, + changeDescription: { + fieldsAdded: [ + { + name: 'tags', + newValue: '', + }, + ], + fieldsUpdated: [], + fieldsDeleted: [], + previousVersion: 3.4, + }, + deleted: false, + }, +]; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.test.ts new file mode 100644 index 000000000000..0eba24e49f93 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2022 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 { cloneDeep } from 'lodash'; +import { sortTagsForCharts } from './DashboardDetailsUtils'; +import { mockCharts, sortedTagsMockCharts } from './DashboardDetailsUtils.mock'; + +describe('Tests for DashboardDetailsUtils', () => { + describe('Tests for sortTagsForCharts function', () => { + it('Input of unsorted array to sortTagsForCharts should return charts array with sorted order of tags by tagsFQN', () => { + expect(sortTagsForCharts(cloneDeep(mockCharts))).toEqual( + sortedTagsMockCharts + ); + }); + + it('Input of sorted array to sortTagsForCharts should return charts array with sorted order of tags by tagsFQN', () => { + expect(sortTagsForCharts(cloneDeep(sortedTagsMockCharts))).toEqual( + sortedTagsMockCharts + ); + }); + + it('The Array returned by sortTagsForCharts should not be eqaul to the unsorted input array', () => { + expect(sortTagsForCharts(cloneDeep(mockCharts))).not.toEqual(mockCharts); + }); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts index 510be0192b09..442be6e9941e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts @@ -12,6 +12,8 @@ */ import { TabSpecificField } from '../enums/entity.enum'; +import { ChartType } from '../pages/DashboardDetailsPage/DashboardDetailsPage.component'; +import { sortTagsCaseInsensitive } from './CommonUtils'; export const defaultFields = `${TabSpecificField.OWNER}, ${TabSpecificField.FOLLOWERS}, ${TabSpecificField.TAGS}, ${TabSpecificField.USAGE_SUMMARY}, ${TabSpecificField.CHARTS},${TabSpecificField.EXTENSION}`; @@ -64,3 +66,10 @@ export const getCurrentDashboardTab = (tab: string) => { return currentTab; }; + +export const sortTagsForCharts = (charts: ChartType[]) => { + return charts.map((chart) => ({ + ...chart, + tags: sortTagsCaseInsensitive(chart.tags || []), + })); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx index cafd0032bb4d..b1e89c875650 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx @@ -44,6 +44,7 @@ import { TagLabel } from '../generated/type/tagLabel'; import { getPartialNameFromTableFQN, getTableFQNFromColumnFQN, + sortTagsCaseInsensitive, } from './CommonUtils'; import { getGlossaryPath, getSettingPath } from './RouterUtils'; import { ordinalize } from './StringsUtils'; @@ -262,7 +263,10 @@ export const getEntityIcon = (indexType: string) => { export const makeRow = (column: Column) => { return { description: column.description || '', - tags: column?.tags || [], + // Sorting tags as the response of PATCH request does not return the sorted order + // of tags, but is stored in sorted manner in the database + // which leads to wrong PATCH payload sent after further tags removal + tags: sortTagsCaseInsensitive(column.tags || []), key: column?.name, ...column, };