mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-25 17:37:57 +00:00
Fix : User Profile edit widgets and Entity Popover Card (#20535)
* fix user profile edit widget chips * minor fix for inherited domains tag * fix entity popover card style issue
This commit is contained in:
parent
3ea4dbd10e
commit
9d19bd3eca
@ -28,6 +28,7 @@ import {
|
||||
getEntityName,
|
||||
getEntityReferenceListFromEntities,
|
||||
} from '../../../../utils/EntityUtils';
|
||||
import { TagRenderer } from '../../../common/TagRenderer/TagRenderer';
|
||||
import { PersonaSelectableListProps } from './PersonaSelectableList.interface';
|
||||
|
||||
export const PersonaListItemRenderer = (props: EntityReference) => {
|
||||
@ -210,6 +211,7 @@ export const PersonaSelectableList = ({
|
||||
popupClassName="persona-custom-dropdown-class"
|
||||
ref={dropdownRef as any}
|
||||
style={{ width: '100%' }}
|
||||
tagRender={TagRenderer}
|
||||
onChange={(selectedIds) => {
|
||||
const selectedPersonasList = selectOptions.filter((persona) =>
|
||||
selectedIds.includes(persona.id)
|
||||
|
@ -21,6 +21,7 @@ import { TeamHierarchy } from '../../../../generated/entity/teams/teamHierarchy'
|
||||
import { getTeamsHierarchy } from '../../../../rest/teamsAPI';
|
||||
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||
import { showErrorToast } from '../../../../utils/ToastUtils';
|
||||
import { TagRenderer } from '../../../common/TagRenderer/TagRenderer';
|
||||
import { TeamsSelectableProps } from './TeamsSelectable.interface';
|
||||
|
||||
const TeamsSelectableNew = forwardRef<any, TeamsSelectableProps>(
|
||||
@ -124,6 +125,7 @@ const TeamsSelectableNew = forwardRef<any, TeamsSelectableProps>(
|
||||
ref={ref as any}
|
||||
showCheckedStrategy={TreeSelect.SHOW_CHILD}
|
||||
style={{ width: '100%' }}
|
||||
tagRender={TagRenderer}
|
||||
treeData={teamsTree}
|
||||
treeLine={{ showLeafIcon }}
|
||||
treeNodeFilterProp="title"
|
||||
|
@ -231,7 +231,7 @@ const UserProfileRoles = ({
|
||||
dropdownMatchSelectWidth={false}
|
||||
filterOption={handleSearchFilterOption}
|
||||
loading={isLoading}
|
||||
maxTagCount={4}
|
||||
maxTagCount={3}
|
||||
maxTagPlaceholder={(omittedValues) => (
|
||||
<span className="max-tag-text">
|
||||
{t('label.plus-count-more', {
|
||||
|
@ -151,7 +151,7 @@ const UserProfileTeams = ({
|
||||
<TeamsSelectableNew
|
||||
filterJoinable
|
||||
handleDropdownChange={handleDropdownChange}
|
||||
maxValueCount={4}
|
||||
maxValueCount={3}
|
||||
ref={teamsSelectableRef}
|
||||
selectedTeams={selectedTeams}
|
||||
onSelectionChange={setSelectedTeams}
|
||||
|
@ -332,7 +332,7 @@
|
||||
|
||||
.ant-select-item-option-selected,
|
||||
.ant-select-selection-item {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ant-select:not(.ant-select-customize-input) {
|
||||
@ -468,3 +468,36 @@
|
||||
vertical-align: text-top;
|
||||
}
|
||||
}
|
||||
.selected-chip-tag-remove {
|
||||
margin-left: 4px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
padding: 0 4px;
|
||||
border: none;
|
||||
background: none;
|
||||
svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
color: @grey-4;
|
||||
}
|
||||
}
|
||||
.domain-link {
|
||||
max-width: 180px;
|
||||
display: inline-block;
|
||||
|
||||
.domain-link-name {
|
||||
color: @text-color;
|
||||
font-weight: 400;
|
||||
|
||||
&:hover {
|
||||
color: @blue-9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 140px;
|
||||
}
|
||||
|
@ -51,7 +51,9 @@ const Chip = ({
|
||||
item.fullyQualifiedName ?? ''
|
||||
)}>
|
||||
{icon}
|
||||
<Typography.Text className="text-left chip-tag-link">
|
||||
<Typography.Text
|
||||
className="text-left chip-tag-link chip-name"
|
||||
ellipsis={{ tooltip: getEntityName(item) }}>
|
||||
{getEntityName(item)}
|
||||
</Typography.Text>
|
||||
</Link>
|
||||
@ -73,7 +75,7 @@ const Chip = ({
|
||||
return (
|
||||
<Row
|
||||
wrap
|
||||
className="align-middle d-flex flex-col flex-start justify-center"
|
||||
className="align-middle d-flex flex-col flex-start justify-center chip-container"
|
||||
data-testid="chip-container"
|
||||
gutter={[20, 0]}>
|
||||
{(isExpanded ? data : data.slice(0, USER_DATA_SIZE)).map(getChipElement)}
|
||||
|
@ -11,6 +11,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
@import (reference) url('../../../styles/variables.less');
|
||||
.chip-container {
|
||||
width: 180px;
|
||||
|
||||
.chip-tag-link {
|
||||
font-weight: 400;
|
||||
@ -21,6 +23,8 @@
|
||||
color: @blue-9;
|
||||
text-decoration: none;
|
||||
}
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.chip-text {
|
||||
@ -33,6 +37,7 @@
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.no-data-chip-placeholder {
|
||||
color: @text-color-tertiary;
|
||||
}
|
||||
|
@ -134,7 +134,8 @@ export const DomainLabelNew = ({
|
||||
domain,
|
||||
domainDisplayName,
|
||||
showDomainHeading,
|
||||
'chip-tag-link'
|
||||
'chip-tag-link',
|
||||
true
|
||||
)}
|
||||
{inheritedIcon && (
|
||||
<div className="d-flex">{inheritedIcon}</div>
|
||||
|
@ -91,7 +91,7 @@ const DomainSelectableListNew = ({
|
||||
}
|
||||
};
|
||||
|
||||
const [popoverHeight, setPopoverHeight] = useState<number>(136);
|
||||
const [popoverHeight, setPopoverHeight] = useState<number>(156);
|
||||
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -37,6 +37,7 @@ import { getEntityReferenceFromEntity } from '../../../utils/EntityUtils';
|
||||
import { findItemByFqn } from '../../../utils/GlossaryUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import Loader from '../Loader/Loader';
|
||||
import { TagRenderer } from '../TagRenderer/TagRenderer';
|
||||
import './domain-selectable.less';
|
||||
import {
|
||||
DomainSelectableTreeProps,
|
||||
@ -227,7 +228,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
className="custom-domain-edit-select"
|
||||
dropdownRender={() => treeContent}
|
||||
dropdownStyle={{ maxHeight: '200px' }}
|
||||
maxTagCount={2}
|
||||
maxTagCount={3}
|
||||
maxTagPlaceholder={(omittedValues) => (
|
||||
<span className="max-tag-text">
|
||||
{t('label.plus-count-more', { count: omittedValues.length })}
|
||||
@ -241,7 +242,7 @@ const DomainSelectablTreeNew: FC<DomainSelectableTreeProps> = ({
|
||||
placeholder="Select a domain"
|
||||
popupClassName="domain-custom-dropdown-class"
|
||||
ref={dropdownRef as any}
|
||||
style={{}}
|
||||
tagRender={TagRenderer}
|
||||
value={
|
||||
selectedDomains
|
||||
?.map((domain) => domain.fullyQualifiedName)
|
||||
|
@ -262,7 +262,7 @@ const EntityPopOverCard: FC<Props> = ({
|
||||
}) => {
|
||||
return (
|
||||
<Popover
|
||||
align={{ targetOffset: [0, -10] }}
|
||||
align={{ targetOffset: [0, 10] }}
|
||||
content={
|
||||
<PopoverContent
|
||||
entityFQN={entityFQN}
|
||||
|
@ -25,4 +25,8 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
&.ant-popover-placement-top {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 { fireEvent, render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { TagRenderer } from './TagRenderer';
|
||||
|
||||
describe('TagRenderer', () => {
|
||||
const mockOnClose = jest.fn();
|
||||
const defaultProps = {
|
||||
label: 'Test Label',
|
||||
value: 'test-value',
|
||||
closable: true,
|
||||
onClose: mockOnClose,
|
||||
disabled: false,
|
||||
onMouseDown: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render the label', () => {
|
||||
render(<TagRenderer {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Test Label')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should truncate long labels and add ellipsis', () => {
|
||||
const longLabel = 'test persona test test persona 5';
|
||||
render(<TagRenderer {...defaultProps} label={longLabel} />);
|
||||
|
||||
expect(screen.getByText('test persona...')).toBeInTheDocument();
|
||||
expect(screen.getByTitle(longLabel)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render close button when closable is true', () => {
|
||||
render(<TagRenderer {...defaultProps} />);
|
||||
|
||||
expect(screen.getByRole('button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call onClose when close button is clicked', () => {
|
||||
render(<TagRenderer {...defaultProps} />);
|
||||
|
||||
const closeButton = screen.getByRole('button');
|
||||
fireEvent.click(closeButton);
|
||||
|
||||
expect(mockOnClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 { CloseOutlined } from '@ant-design/icons';
|
||||
import type { CustomTagProps } from 'rc-select/lib/BaseSelect';
|
||||
import React from 'react';
|
||||
|
||||
export const TagRenderer = (props: CustomTagProps) => {
|
||||
const { label, closable, onClose } = props;
|
||||
const displayLabel =
|
||||
typeof label === 'string' && label.length > 12
|
||||
? `${label.substring(0, 12)}...`
|
||||
: label;
|
||||
|
||||
return (
|
||||
<span className="ant-select-selection-item" title={(label as string) ?? ''}>
|
||||
{displayLabel}
|
||||
{closable && (
|
||||
<button className="selected-chip-tag-remove" onClick={onClose}>
|
||||
<CloseOutlined />
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
@ -229,23 +229,39 @@ export const renderDomainLink = (
|
||||
domain: EntityReference,
|
||||
domainDisplayName: ReactNode,
|
||||
showDomainHeading: boolean,
|
||||
textClassName?: string
|
||||
) => (
|
||||
textClassName?: string,
|
||||
trimLink?: boolean
|
||||
) => {
|
||||
const displayName = isUndefined(domainDisplayName)
|
||||
? getEntityName(domain)
|
||||
: domainDisplayName;
|
||||
|
||||
return (
|
||||
<Tooltip title={domainDisplayName ?? getEntityName(domain)}>
|
||||
<Link
|
||||
className={classNames(
|
||||
'no-underline domain-link domain-link-text font-medium',
|
||||
{ 'text-sm': !showDomainHeading },
|
||||
{
|
||||
'text-sm': !showDomainHeading,
|
||||
'text-truncate': trimLink,
|
||||
},
|
||||
textClassName
|
||||
)}
|
||||
data-testid="domain-link"
|
||||
to={getDomainPath(domain?.fullyQualifiedName)}>
|
||||
{isUndefined(domainDisplayName)
|
||||
? getEntityName(domain)
|
||||
: domainDisplayName}
|
||||
{trimLink ? (
|
||||
<Typography.Text
|
||||
className="domain-link-name"
|
||||
ellipsis={{ tooltip: false }}>
|
||||
{displayName}
|
||||
</Typography.Text>
|
||||
) : (
|
||||
<>{displayName}</>
|
||||
)}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export const initializeDomainEntityRef = (
|
||||
domains: EntityReference[],
|
||||
|
Loading…
x
Reference in New Issue
Block a user