diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts index bf400beb74b9..6692dd3b50f7 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts @@ -43,18 +43,27 @@ import { approveTagsTask, assignTagToGlossaryTerm, changeTermHierarchyFromModal, + clickSaveButton, confirmationDragAndDropGlossary, createDescriptionTaskForGlossary, createGlossary, createGlossaryTerms, createTagTaskForGlossary, deleteGlossaryOrGlossaryTerm, + deselectColumns, + dragAndDropColumn, dragAndDropTerm, + filterStatus, goToAssetsTab, + openColumnDropdown, renameGlossaryTerm, selectActiveGlossary, selectActiveGlossaryTerm, + selectColumns, + toggleAllColumnsSelection, validateGlossaryTerm, + verifyAllColumns, + verifyColumnsVisibility, verifyGlossaryDetails, verifyGlossaryTermAssets, } from '../../utils/glossary'; @@ -924,6 +933,141 @@ test.describe('Glossary tests', () => { } }); + test('Column selection and visibility for Glossary Terms table', async ({ + browser, + }) => { + const { page, afterAction, apiContext } = await performAdminLogin(browser); + const glossary1 = new Glossary(); + const glossaryTerm1 = new GlossaryTerm(glossary1); + const glossaryTerm2 = new GlossaryTerm(glossary1); + glossary1.data.terms = [glossaryTerm1, glossaryTerm2]; + + try { + await glossary1.create(apiContext); + await glossaryTerm1.create(apiContext); + await glossaryTerm2.create(apiContext); + await sidebarClick(page, SidebarItem.GLOSSARY); + await selectActiveGlossary(page, glossary1.data.displayName); + + await test.step( + 'Open column dropdown and select columns and check if they are visible', + async () => { + await openColumnDropdown(page); + const checkboxLabels = ['Reviewer', 'Synonyms']; + await selectColumns(page, checkboxLabels); + await clickSaveButton(page); + await verifyColumnsVisibility(page, checkboxLabels, true); + } + ); + + await test.step( + 'Open column dropdown and deselect columns and check if they are hidden', + async () => { + await openColumnDropdown(page); + const checkboxLabels = ['Reviewer', 'Owners']; + await deselectColumns(page, checkboxLabels); + await clickSaveButton(page); + await verifyColumnsVisibility(page, checkboxLabels, false); + } + ); + + await test.step('All columns selection', async () => { + await toggleAllColumnsSelection(page, true); + const tableColumns = [ + 'TERMS', + 'DESCRIPTION', + 'REVIEWER', + 'SYNONYMS', + 'OWNERS', + 'STATUS', + 'ACTIONS', + ]; + await verifyAllColumns(page, tableColumns, true); + }); + } finally { + await glossaryTerm1.delete(apiContext); + await glossaryTerm2.delete(apiContext); + await glossary1.delete(apiContext); + await afterAction(); + } + }); + + test('Glossary Terms Table Status filtering', async ({ browser }) => { + const { page, afterAction, apiContext } = await performAdminLogin(browser); + const glossary1 = new Glossary(); + const glossaryTerm1 = new GlossaryTerm(glossary1); + const glossaryTerm2 = new GlossaryTerm(glossary1); + glossary1.data.terms = [glossaryTerm1, glossaryTerm2]; + + try { + await glossary1.create(apiContext); + await glossaryTerm1.create(apiContext); + await glossaryTerm2.create(apiContext); + await sidebarClick(page, SidebarItem.GLOSSARY); + await selectActiveGlossary(page, glossary1.data.displayName); + + await test.step( + 'Deselect status and check if the table has filtered rows', + async () => { + await filterStatus(page, ['Draft'], ['Approved']); + } + ); + + await test.step( + 'Re-select the status and check if it appears again', + async () => { + await filterStatus(page, ['Draft'], ['Draft', 'Approved']); + } + ); + } finally { + await glossaryTerm1.delete(apiContext); + await glossaryTerm2.delete(apiContext); + await glossary1.delete(apiContext); + await afterAction(); + } + }); + + test('Column dropdown drag-and-drop functionality for Glossary Terms table', async ({ + browser, + }) => { + const { page, afterAction, apiContext } = await performAdminLogin(browser); + const glossary1 = new Glossary(); + const glossaryTerm1 = new GlossaryTerm(glossary1); + const glossaryTerm2 = new GlossaryTerm(glossary1); + glossary1.data.terms = [glossaryTerm1, glossaryTerm2]; + + try { + await glossary1.create(apiContext); + await glossaryTerm1.create(apiContext); + await glossaryTerm2.create(apiContext); + await sidebarClick(page, SidebarItem.GLOSSARY); + await selectActiveGlossary(page, glossary1.data.displayName); + await openColumnDropdown(page); + const dragColumn = 'Owners'; + const dropColumn = 'Status'; + await dragAndDropColumn(page, dragColumn, dropColumn); + const saveButton = page.locator('.ant-btn-primary', { + hasText: 'Save', + }); + await saveButton.click(); + const columnHeaders = await page.locator('thead th'); + const columnText = await columnHeaders.allTextContents(); + + expect(columnText).toEqual([ + 'Terms', + 'Description', + 'Status', + 'Owners', + 'Actions', + ]); + } finally { + await glossaryTerm1.delete(apiContext); + await glossaryTerm2.delete(apiContext); + await glossary1.delete(apiContext); + await afterAction(); + } + }); + test.afterAll(async ({ browser }) => { const { afterAction, apiContext } = await performAdminLogin(browser); await user1.delete(apiContext); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts index ee2abfe27eb9..22cba32b692d 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/glossary.ts @@ -1004,3 +1004,152 @@ export const approveTagsTask = async ( await expect(tagVisibility).toBe(true); }; + +export async function openColumnDropdown(page: Page): Promise { + const dropdownButton = page.getByTestId('glossary-column-dropdown'); + + await expect(dropdownButton).toBeVisible(); + + await dropdownButton.click(); +} + +export async function selectColumns( + page: Page, + checkboxLabels: string[] +): Promise { + for (const label of checkboxLabels) { + const checkbox = page.locator('.glossary-dropdown-label', { + hasText: label, + }); + await checkbox.click(); + } +} + +export async function deselectColumns( + page: Page, + checkboxLabels: string[] +): Promise { + for (const label of checkboxLabels) { + const checkbox = page.locator('.glossary-dropdown-label', { + hasText: label, + }); + await checkbox.click(); + } +} + +export async function clickSaveButton(page: Page): Promise { + const saveButton = page.locator('.ant-btn-primary', { + hasText: 'Save', + }); + await saveButton.click(); +} + +export async function verifyColumnsVisibility( + page: Page, + checkboxLabels: string[], + shouldBeVisible: boolean +): Promise { + const glossaryTermsTable = page.getByTestId('glossary-terms-table'); + + await expect(glossaryTermsTable).toBeVisible(); + + for (const label of checkboxLabels) { + const termsColumnHeader = glossaryTermsTable.locator('th', { + hasText: label, + }); + if (shouldBeVisible) { + await expect(termsColumnHeader).toBeVisible(); + } else { + await expect(termsColumnHeader).toBeHidden(); + } + } +} + +export async function toggleAllColumnsSelection( + page: Page, + isSelected: boolean +): Promise { + const dropdownButton = page.getByTestId('glossary-column-dropdown'); + + await expect(dropdownButton).toBeVisible(); + + await dropdownButton.click(); + + const checkboxLabel = 'All'; + const checkbox = page.locator('.custom-glossary-col-sel-checkbox', { + hasText: checkboxLabel, + }); + if (isSelected) { + await checkbox.click(); + } + await clickSaveButton(page); +} + +export async function verifyAllColumns( + page: Page, + tableColumns: string[], + shouldBeVisible: boolean +): Promise { + const glossaryTermsTable = page.getByTestId('glossary-terms-table'); + + await expect(glossaryTermsTable).toBeVisible(); + + for (const columnHeader of tableColumns) { + const termsColumnHeader = glossaryTermsTable.locator('th', { + hasText: columnHeader, + }); + + if (shouldBeVisible) { + await expect(termsColumnHeader).toBeVisible(); + } else { + if (columnHeader !== 'TERMS') { + await expect(termsColumnHeader).not.toBeVisible(); + } else { + await expect(termsColumnHeader).toBeVisible(); + } + } + } +} +export const filterStatus = async ( + page: Page, + statusLabels: string[], + expectedStatus: string[] +): Promise => { + const dropdownButton = page.getByTestId('glossary-status-dropdown'); + await dropdownButton.click(); + + for (const label of statusLabels) { + const checkbox = page.locator('.glossary-dropdown-label', { + hasText: label, + }); + await checkbox.click(); + } + + const saveButton = page.locator('.ant-btn-primary', { + hasText: 'Save', + }); + await saveButton.click(); + + const glossaryTermsTable = page.getByTestId('glossary-terms-table'); + const rows = glossaryTermsTable.locator('tbody tr'); + const statusColumnIndex = 3; + + for (let i = 0; i < (await rows.count()); i++) { + const statusCell = rows + .nth(i) + .locator(`td:nth-child(${statusColumnIndex + 1})`); + const statusText = await statusCell.textContent(); + + expect(expectedStatus).toContain(statusText); + } +}; + +export const dragAndDropColumn = async ( + page: Page, + dragColumn: string, + dropColumn: string +) => { + await page + .locator('.draggable-menu-item', { hasText: dragColumn }) + .dragTo(page.locator('.draggable-menu-item', { hasText: dropColumn })); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/menu-duo.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/menu-duo.svg new file mode 100644 index 000000000000..f6641fb96ead --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/menu-duo.svg @@ -0,0 +1,3 @@ + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryColumnsSelectionDropdown/DraggableMenuItem.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryColumnsSelectionDropdown/DraggableMenuItem.component.tsx new file mode 100644 index 000000000000..76c77f1f3b4c --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryColumnsSelectionDropdown/DraggableMenuItem.component.tsx @@ -0,0 +1,84 @@ +/* + * Copyright 2024 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 { Checkbox } from 'antd'; +import React, { useCallback } from 'react'; +import { useDrag, useDrop } from 'react-dnd'; +import { ReactComponent as ColumnDragIcon } from '../../../assets/svg/menu-duo.svg'; + +interface DraggableMenuItemProps { + option: { value: string; label: string }; + index: number; + options: { value: string; label: string }[]; + onMoveItem: (updatedList: { value: string; label: string }[]) => void; + selectedOptions: string[]; + onSelect: (key: string, checked: boolean, type: 'columns' | 'status') => void; +} +const DraggableMenuItem: React.FC = ({ + option, + index, + options, + onMoveItem, + selectedOptions, + onSelect, +}) => { + const moveDropdownMenuItem = useCallback( + (fromIndex: number, toIndex: number) => { + const updatedList = [...options]; + const [movedItem] = updatedList.splice(fromIndex, 1); + updatedList.splice(toIndex, 0, movedItem); + onMoveItem(updatedList); + }, + [options, onMoveItem] + ); + const [{ isDragging }, drag] = useDrag({ + type: 'CHECKBOX', + item: { index }, + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + }), + }); + + const [, drop] = useDrop({ + accept: 'CHECKBOX', + hover: (draggedItem: any) => { + if (draggedItem.index !== index) { + moveDropdownMenuItem(draggedItem.index, index); + draggedItem.index = index; + } + }, + }); + + return ( +
{ + drag(drop(node)); + }}> + + onSelect(option.value, e.target.checked, 'columns')}> +

{option.label}

+
+
+ ); +}; + +export default DraggableMenuItem; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx index 6897cd75d051..f10487faa84d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx @@ -11,10 +11,24 @@ * limitations under the License. */ -import { FilterOutlined } from '@ant-design/icons'; +import { DownOutlined } from '@ant-design/icons'; import Icon from '@ant-design/icons/lib/components/Icon'; -import { Button, Col, Modal, Row, Space, TableProps, Tooltip } from 'antd'; -import { ColumnsType, ExpandableConfig } from 'antd/lib/table/interface'; +import { + Button, + Checkbox, + Col, + Dropdown, + Modal, + Row, + Space, + TableProps, + Tooltip, +} from 'antd'; +import { + ColumnsType, + ColumnType, + ExpandableConfig, +} from 'antd/lib/table/interface'; import { AxiosError } from 'axios'; import classNames from 'classnames'; import { compare } from 'fast-json-patch'; @@ -38,6 +52,7 @@ import StatusBadge from '../../../components/common/StatusBadge/StatusBadge.comp import { API_RES_MAX_SIZE, DE_ACTIVE_COLOR, + NO_DATA_PLACEHOLDER, TEXT_BODY_COLOR, } from '../../../constants/constants'; import { GLOSSARIES_DOCS } from '../../../constants/docs.constants'; @@ -49,7 +64,6 @@ import { GlossaryTerm, Status, } from '../../../generated/entity/data/glossaryTerm'; -import { useApplicationStore } from '../../../hooks/useApplicationStore'; import { getFirstLevelGlossaryTerms, getGlossaryTerms, @@ -64,13 +78,14 @@ import { findExpandableKeysForArray, findGlossaryTermByFqn, StatusClass, - StatusFilters, } from '../../../utils/GlossaryUtils'; import { getGlossaryPath } from '../../../utils/RouterUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; import { DraggableBodyRowProps } from '../../common/Draggable/DraggableBodyRowProps.interface'; import Loader from '../../common/Loader/Loader'; import Table from '../../common/Table/Table'; +import TagButton from '../../common/TagButton/TagButton.component'; +import DraggableMenuItem from '../GlossaryColumnsSelectionDropdown/DraggableMenuItem.component'; import { ModifiedGlossary, useGlossaryStore } from '../useGlossary.store'; import { GlossaryTermTabProps, @@ -89,7 +104,6 @@ const GlossaryTermTab = ({ }: GlossaryTermTabProps) => { const { activeGlossary, glossaryChildTerms, setGlossaryChildTerms } = useGlossaryStore(); - const { theme } = useApplicationStore(); const { t } = useTranslation(); const glossaryTerms = (glossaryChildTerms as ModifiedGlossaryTerm[]) ?? []; @@ -100,6 +114,19 @@ const GlossaryTermTab = ({ const [isTableLoading, setIsTableLoading] = useState(false); const [isTableHovered, setIsTableHovered] = useState(false); const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [isStatusDropdownVisible, setIsStatusDropdownVisible] = + useState(false); + const statusOptions = useMemo( + () => + Object.values(Status).map((status) => ({ value: status, label: status })), + [] + ); + const [statusDropdownSelection, setStatusDropdownSelections] = useState< + string[] + >(['Approved', 'Draft']); + const [selectedStatus, setSelectedStatus] = useState([ + ...statusDropdownSelection, + ]); const glossaryTermStatus: Status | null = useMemo(() => { if (!isGlossary) { @@ -162,6 +189,36 @@ const GlossaryTermTab = ({ {t('label.no-description')} ), }, + { + title: t('label.reviewer'), + dataIndex: 'reviewers', + key: 'reviewers', + width: '33%', + render: (reviewers: EntityReference[]) => ( + + ), + }, + { + title: t('label.synonym-plural'), + dataIndex: 'synonyms', + key: 'synonyms', + width: '33%', + render: (synonyms: string[]) => { + return isEmpty(synonyms) ? ( +
{NO_DATA_PLACEHOLDER}
+ ) : ( +
+ {synonyms.map((synonym: string) => ( + + ))} +
+ ); + }, + }, { title: t('label.owner-plural'), dataIndex: 'owners', @@ -174,14 +231,6 @@ const GlossaryTermTab = ({ dataIndex: 'status', key: 'status', width: '12%', - filterIcon: (filtered) => ( - - ), - filters: StatusFilters, render: (_, record) => { const status = record.status ?? Status.Approved; @@ -249,6 +298,303 @@ const GlossaryTermTab = ({ return data; }, [glossaryTerms, permissions]); + const listOfVisibleColumns = useMemo(() => { + return ['name', 'description', 'owners', 'status', 'new-term']; + }, []); + + const defaultCheckedList = useMemo( + () => + columns.reduce( + (acc, column) => + listOfVisibleColumns.includes(column.key as string) + ? [...acc, column.key as string] + : acc, + [] + ), + [columns, listOfVisibleColumns] + ); + + const [selectedColumns, setSelectedColumns] = + useState(defaultCheckedList); + const [columnDropdownSelections, setColumnDropdownSelections] = useState< + string[] + >([...selectedColumns]); + + const [isDropdownVisible, setIsDropdownVisible] = useState(false); + + const [options, setOptions] = useState<{ value: string; label: string }[]>( + columns.reduce<{ value: string; label: string }[]>( + (acc, { key, title }) => + key !== 'name' + ? [...acc, { label: title as string, value: key as string }] + : acc, + [] + ) + ); + + const newColumns = useMemo(() => { + return columns.map((item) => ({ + ...item, + hidden: !selectedColumns.includes(item.key as string), + })); + }, [columns, selectedColumns]); + + const rearrangedColumns = useMemo( + () => + newColumns + .filter((column) => !column.hidden) + .sort((a, b) => { + const aIndex = options.findIndex( + (option: { value: string; label: string }) => option.value === a.key + ); + const bIndex = options.findIndex( + (option: { value: string; label: string }) => option.value === b.key + ); + + return aIndex - bIndex; + }), + [newColumns] + ); + + const handleColumnSelectionDropdownSave = useCallback(() => { + setSelectedColumns(columnDropdownSelections); + setIsDropdownVisible(false); + }, [columnDropdownSelections]); + + const handleColumnSelectionDropdownCancel = useCallback(() => { + setColumnDropdownSelections(selectedColumns); + setIsDropdownVisible(false); + }, [selectedColumns]); + + const handleMoveItem = (updatedList: { value: string; label: string }[]) => { + setOptions(updatedList); + setColumnDropdownSelections((prevCheckedList) => { + const updatedCheckedList = prevCheckedList.map((item) => { + const index = updatedList.findIndex((option) => option.value === item); + + return updatedList[index]?.value || item; + }); + + return updatedCheckedList; + }); + }; + const handleCheckboxChange = useCallback( + (key: string, checked: boolean, type: 'columns' | 'status') => { + const setCheckedList = + type === 'columns' + ? setColumnDropdownSelections + : setStatusDropdownSelections; + + const optionsToUse = + type === 'columns' + ? (columns as ColumnType[]) + : (statusOptions as { value: string }[]); + + if (key === 'all') { + if (checked) { + const newCheckedList = [ + 'all', + ...optionsToUse.map((option) => { + return type === 'columns' + ? String((option as ColumnType).key) + : (option as { value: string }).value ?? ''; + }), + ]; + setCheckedList(newCheckedList); + } else { + setCheckedList([type === 'columns' ? 'name' : 'Draft']); + } + } else { + setCheckedList((prev: string[]) => { + const newCheckedList = checked + ? [...prev, key] + : prev.filter((item) => item !== key); + + const allChecked = + type === 'columns' + ? (optionsToUse as ColumnType[]).every( + (opt) => newCheckedList.includes(String(opt.key)) + ) + : (optionsToUse as { value: string }[]).every((opt) => + newCheckedList.includes(opt.value ?? '') + ); + + if (allChecked) { + return ['all', ...newCheckedList]; + } + + return newCheckedList.filter((item) => item !== 'all'); + }); + } + }, + [ + columns, + statusOptions, + setColumnDropdownSelections, + setStatusDropdownSelections, + ] + ); + + const menu = useMemo( + () => ({ + items: [ + { + key: 'addColumn', + label: ( +
+

{t('label.add-column')}

+ + col.key === 'name') + .every(({ key }) => + columnDropdownSelections.includes(key as string) + )} + className="custom-glossary-col-sel-checkbox m-l-lg p-l-md" + key="all" + value="all" + onChange={(e) => + handleCheckboxChange('all', e.target.checked, 'columns') + }> + {t('label.all')} + + {options.map( + (option: { value: string; label: string }, index: number) => ( +
+ +
+ ) + )} +
+
+ ), + }, + { + key: 'divider', + type: 'divider', + }, + { + key: 'actions', + label: ( +
+ + + + +
+ ), + }, + ], + }), + [ + columnDropdownSelections, + columns, + options, + handleCheckboxChange, + handleColumnSelectionDropdownSave, + handleColumnSelectionDropdownCancel, + ] + ); + const handleStatusSelectionDropdownSave = () => { + setSelectedStatus(statusDropdownSelection); + setIsStatusDropdownVisible(false); + }; + + const handleStatusSelectionDropdownCancel = () => { + setStatusDropdownSelections(selectedStatus); + setIsStatusDropdownVisible(false); + }; + const statusDropdownMenu = useMemo( + () => ({ + items: [ + { + key: 'statusSelection', + label: ( +
+ + + handleCheckboxChange('all', e.target.checked, 'status') + }> +

{t('label.all')}

+
+ {statusOptions.map((option) => ( +
+ + handleCheckboxChange( + option.value, + e.target.checked, + 'status' + ) + }> +

{option.label}

+
+
+ ))} +
+
+ ), + }, + { + key: 'divider', + type: 'divider', + }, + { + key: 'actions', + label: ( +
+ + + + +
+ ), + }, + ], + }), + [ + statusDropdownSelection, + statusOptions, + handleStatusSelectionDropdownSave, + handleStatusSelectionDropdownCancel, + ] + ); + const handleAddGlossaryTermClick = () => { onAddGlossaryTerm( !isGlossary ? (activeGlossary as GlossaryTerm) : undefined @@ -468,6 +814,52 @@ const GlossaryTermTab = ({ {isAllExpanded ? t('label.collapse-all') : t('label.expand-all')} + { + const customContainer = trigger.closest( + '.custom-glossary-dropdown-menu.status-dropdown' + ); + + return customContainer as HTMLElement; + }} + menu={statusDropdownMenu} + open={isStatusDropdownVisible} + trigger={['click']} + onOpenChange={setIsStatusDropdownVisible}> + + + + { + const customContainer = trigger.closest( + '.custom-glossary-dropdown-menu' + ); + + return customContainer as HTMLElement; + }} + menu={menu} + open={isDropdownVisible} + trigger={['click']} + onOpenChange={setIsDropdownVisible}> + + + {glossaryTerms.length > 0 ? ( @@ -477,9 +869,12 @@ const GlossaryTermTab = ({ className={classNames('drop-over-background', { 'drop-over-table': isTableHovered, })} - columns={columns} + columns={rearrangedColumns.filter((col) => !col.hidden)} components={TABLE_CONSTANTS} - dataSource={glossaryTerms} + data-testid="glossary-terms-table" + dataSource={glossaryTerms.filter((term) => + selectedStatus.includes(term.status as string) + )} expandable={expandableConfig} loading={isTableLoading} pagination={false} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/glossaryV1.less b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/glossaryV1.less index 71b86837af58..be5a596a22d5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/glossaryV1.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/glossaryV1.less @@ -90,3 +90,102 @@ display: inline-block; } } + +.custom-glossary-col-sel-checkbox { + font-size: 16px; + color: @grey-3; + width: 100%; + + .ant-checkbox-inner { + width: 20px; + height: 20px; + background-color: @white; + border-color: @grey-4; + + &::after { + width: 6px; + height: 10px; + border-color: @purple-2; + border-width: 0 2px 2px 0; + } + } + + .ant-checkbox-checked .ant-checkbox-inner { + background-color: @white; + border-color: @grey-4; + } + + .ant-checkbox-wrapper:hover .ant-checkbox-inner, + .ant-checkbox:hover .ant-checkbox-inner, + .ant-checkbox-input:focus + .ant-checkbox-inner { + border-color: @grey-4; + } +} + +.custom-glossary-dropdown-menu { + .ant-dropdown-menu { + background-color: @white; + border-color: @grey-4; + } + + .ant-dropdown-menu-item { + padding-left: 0px; + padding-right: 0px; + } + .ant-dropdown-menu-item:hover { + background-color: @white; + color: inherit; + } +} +.glossary-col-sel-dropdown-title { + font-size: 14px; + line-height: 21px; + color: @grey-4; +} + +.glossary-col-sel-checkbox-group { + display: flex; + flex-direction: column; + width: 100%; + background-color: @white; + border-color: @grey-3; +} +.status-dropdown { + .ant-dropdown-menu-item { + padding-left: 8px; + padding-right: 8px; + } +} +.draggable-menu-item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + transition: background-color 0.3s ease, opacity 0.3s ease; + width: 100%; + box-sizing: border-box; + padding: 0px 8px; + box-sizing: border-box; +} + +.draggable-menu-item.dragging { + background-color: #f0f0f0; + opacity: 0.8; + z-index: 9999; +} + +.glossary-dropdown-label { + font-size: 14px; + line-height: 21px; + color: @grey-4; +} + +.custom-status-dropdown-btn { + background-color: @white; + outline: 1px solid @grey-2; + color: @black; +} + +.glossary-col-dropdown-drag-icon { + margin-left: 8px; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index dddc5e6d5b37..059740e99e4a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -28,6 +28,7 @@ "add": "Hinzufügen", "add-a-new-service": "Neuen Service hinzufügen", "add-an-image": "Add an image", + "add-column": "Add Column", "add-custom-entity-property": "Benutzerdefinierte Eigenschaft zu {{entity}} hinzufügen", "add-deploy": "Hinzufügen und Bereitstellen", "add-entity": "{{entity}} hinzufügen", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 8ccb1d6d8f03..bce10112f41f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -28,6 +28,7 @@ "add": "Add", "add-a-new-service": "Add a New Service", "add-an-image": "Add an image", + "add-column": "Add Column", "add-custom-entity-property": "Add Custom {{entity}} Property", "add-deploy": "Add & Deploy", "add-entity": "Add {{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index deb3374652d8..b11f872b9054 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -28,6 +28,7 @@ "add": "Añadir", "add-a-new-service": "Añadir un nuevo servicio", "add-an-image": "Añadir una imagen", + "add-column": "Add Column", "add-custom-entity-property": "Añadir propiedad personalizada de {{entity}}", "add-deploy": "Añadir y desplegar", "add-entity": "Añadir {{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index 6b4363b1bb47..983954aabb08 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -28,6 +28,7 @@ "add": "Ajouter", "add-a-new-service": "Ajouter un Nouveau Service", "add-an-image": "Ajouter une Image", + "add-column": "Add Column", "add-custom-entity-property": "Ajouter une Propriété Personnalisée à {{entity}}", "add-deploy": "Ajouter et Déployer", "add-entity": "Ajouter {{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json index 48a2fd842595..6dae479e8cab 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json @@ -28,6 +28,7 @@ "add": "Engadir", "add-a-new-service": "Engadir un novo servizo", "add-an-image": "Engadir unha imaxe", + "add-column": "Add Column", "add-custom-entity-property": "Engadir unha propiedade personalizada de {{entity}}", "add-deploy": "Engadir e despregar", "add-entity": "Engadir {{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 2c1b2127d9b3..ebc38e89ddb9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -28,6 +28,7 @@ "add": "הוסף", "add-a-new-service": "הוסף שירות חדש", "add-an-image": "הוסף תמונה", + "add-column": "Add Column", "add-custom-entity-property": "הוסף נכס מותאם אישית ל {{entity}}", "add-deploy": "הוסף והפעל", "add-entity": "הוסף {{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index e584a257b600..10d95fc6e31a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -28,6 +28,7 @@ "add": "追加", "add-a-new-service": "新しいサービスを追加", "add-an-image": "Add an image", + "add-column": "Add Column", "add-custom-entity-property": "Add Custom {{entity}} Property", "add-deploy": "追加&デプロイ", "add-entity": "{{entity}}を追加", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index 5cb6fd154cae..304ae6d635f6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -28,6 +28,7 @@ "add": "Toevoegen", "add-a-new-service": "Nieuwe service toevoegen", "add-an-image": "Afbeelding toevoegen", + "add-column": "Add Column", "add-custom-entity-property": "Aangepaste {{entity}} eigenschap toevoegen", "add-deploy": "Toevoegen en deployen", "add-entity": "{{entity}} toevoegen", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json index 13f85472ed35..cc3992554abf 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json @@ -28,6 +28,7 @@ "add": "اضافه کردن", "add-a-new-service": "اضافه کردن یک سرویس جدید", "add-an-image": "اضافه کردن تصویر", + "add-column": "Add Column", "add-custom-entity-property": "اضافه کردن خصوصیت سفارشی {{entity}}", "add-deploy": "اضافه کردن و استقرار", "add-entity": "اضافه کردن {{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 3990bb941a30..d15ccea33a79 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -28,6 +28,7 @@ "add": "Adicionar", "add-a-new-service": "Adicionar um Novo Serviço", "add-an-image": "Adicionar uma imagem", + "add-column": "Add Column", "add-custom-entity-property": "Adicionar Propriedade Personalizada {{entity}}", "add-deploy": "Adicionar & Implementar", "add-entity": "Adicionar {{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index 7230703879ef..b8705f2e7a4b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -28,6 +28,7 @@ "add": "Добавить", "add-a-new-service": "Добавить новый сервис", "add-an-image": "Add an image", + "add-column": "Add Column", "add-custom-entity-property": "Добавить пользовательское свойство {{entity}}", "add-deploy": "Добавить & Развернуть", "add-entity": "Добавить {{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 60f2cd2a3daf..2a3bef7594cd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -28,6 +28,7 @@ "add": "添加", "add-a-new-service": "添加新服务", "add-an-image": "添加图片", + "add-column": "Add Column", "add-custom-entity-property": "添加自定义{{entity}}属性", "add-deploy": "添加部署", "add-entity": "添加{{entity}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts index a51fdf5623f4..2828f847fd7a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts @@ -335,7 +335,11 @@ export const getFirstLevelGlossaryTerms = async (parentFQN: string) => { >(apiUrl, { params: { directChildrenOf: parentFQN, - fields: [TabSpecificField.CHILDREN_COUNT, TabSpecificField.OWNERS], + fields: [ + TabSpecificField.CHILDREN_COUNT, + TabSpecificField.OWNERS, + TabSpecificField.REVIEWERS, + ], limit: 100000, }, });