fix(ui) Misc styling fixes (truncated filter values, updating tag color on clickaway) (#4246)

This commit is contained in:
John Joyce 2022-02-24 10:48:27 -08:00 committed by GitHub
parent 826abb882e
commit 5d0915ec64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 96 additions and 47 deletions

View File

@ -36,7 +36,8 @@ const PlatformInfo = styled.div`
`; `;
const TitleContainer = styled.div` const TitleContainer = styled.div`
margin-bottom: 8px; margin-bottom: 0px;
line-height: 30px;
`; `;
const PreviewImage = styled(Image)` const PreviewImage = styled(Image)`
@ -49,7 +50,7 @@ const PreviewImage = styled(Image)`
const EntityTitle = styled(Typography.Text)<{ $titleSizePx?: number }>` const EntityTitle = styled(Typography.Text)<{ $titleSizePx?: number }>`
&&& { &&& {
margin-bottom: 0; margin-right 8px;
font-size: ${(props) => props.$titleSizePx || 16}px; font-size: ${(props) => props.$titleSizePx || 16}px;
font-weight: 600; font-weight: 600;
vertical-align: middle; vertical-align: middle;
@ -91,7 +92,7 @@ const AvatarContainer = styled.div`
const TagContainer = styled.div` const TagContainer = styled.div`
display: inline-block; display: inline-block;
margin-left: 8px; margin-left: 0px;
margin-top: -2px; margin-top: -2px;
`; `;

View File

@ -1,8 +1,7 @@
import { BookOutlined } from '@ant-design/icons'; import { BookOutlined } from '@ant-design/icons';
import { Tag } from 'antd'; import { Tag, Tooltip } from 'antd';
import * as React from 'react'; import * as React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { import {
AggregationMetadata, AggregationMetadata,
Domain, Domain,
@ -53,24 +52,27 @@ export const SearchFilterLabel = ({ aggregation, field }: Props) => {
if (aggregation.entity?.type === EntityType.Tag) { if (aggregation.entity?.type === EntityType.Tag) {
const tag = aggregation.entity as TagType; const tag = aggregation.entity as TagType;
const displayName = entityRegistry.getDisplayName(EntityType.Tag, tag);
const truncatedDisplayName = displayName.length > 25 ? `${displayName.slice(0, 25)}...` : displayName;
return ( return (
<> <Tooltip title={displayName}>
<StyledTag $colorHash={tag?.urn} $color={tag?.properties?.colorHex}> <StyledTag $colorHash={tag?.urn} $color={tag?.properties?.colorHex}>
{tag?.name} {truncatedDisplayName}
</StyledTag> </StyledTag>
({countText}) ({countText})
</> </Tooltip>
); );
} }
if (aggregation.entity?.type === EntityType.CorpUser) { if (aggregation.entity?.type === EntityType.CorpUser) {
const user = aggregation.entity as CorpUser; const user = aggregation.entity as CorpUser;
const displayName = entityRegistry.getDisplayName(EntityType.CorpUser, user); const displayName = entityRegistry.getDisplayName(EntityType.CorpUser, user);
const truncatedDisplayName = displayName.length > 25 ? `${displayName.slice(0, 25)}...` : displayName;
return ( return (
<> <Tooltip title={displayName}>
<CustomAvatar <CustomAvatar
size={18} size={18}
name={displayName} name={truncatedDisplayName}
photoUrl={user.editableProperties?.pictureLink || undefined} photoUrl={user.editableProperties?.pictureLink || undefined}
useDefaultAvatar={false} useDefaultAvatar={false}
style={{ style={{
@ -78,82 +80,92 @@ export const SearchFilterLabel = ({ aggregation, field }: Props) => {
}} }}
/> />
{displayName} ({countText}) {displayName} ({countText})
</> </Tooltip>
); );
} }
if (aggregation.entity?.type === EntityType.CorpGroup) { if (aggregation.entity?.type === EntityType.CorpGroup) {
const group = aggregation.entity as CorpGroup; const group = aggregation.entity as CorpGroup;
const displayName = entityRegistry.getDisplayName(EntityType.CorpGroup, group);
const truncatedDisplayName = displayName.length > 25 ? `${displayName.slice(0, 25)}...` : displayName;
return ( return (
<> <Tooltip title={displayName}>
<span style={{ marginRight: 8 }}> <span style={{ marginRight: 8 }}>
{entityRegistry.getIcon(EntityType.CorpGroup, 16, IconStyleType.ACCENT)} {entityRegistry.getIcon(EntityType.CorpGroup, 16, IconStyleType.ACCENT)}
</span> </span>
{entityRegistry.getDisplayName(EntityType.CorpGroup, group)} ({countText}) {truncatedDisplayName} ({countText})
</> </Tooltip>
); );
} }
if (aggregation.entity?.type === EntityType.GlossaryTerm) { if (aggregation.entity?.type === EntityType.GlossaryTerm) {
const term = aggregation.entity as GlossaryTerm; const term = aggregation.entity as GlossaryTerm;
const displayName = entityRegistry.getDisplayName(EntityType.GlossaryTerm, term);
const truncatedDisplayName = displayName.length > 25 ? `${displayName.slice(0, 25)}...` : displayName;
return ( return (
<> <Tooltip title={displayName}>
<Tag closable={false}> <Tag closable={false}>
<BookOutlined style={{ marginRight: '3%' }} /> <BookOutlined style={{ marginRight: '3%' }} />
{entityRegistry.getDisplayName(EntityType.GlossaryTerm, term)} {truncatedDisplayName}
</Tag> </Tag>
({countText}) ({countText})
</> </Tooltip>
); );
} }
if (aggregation.entity?.type === EntityType.DataPlatform) { if (aggregation.entity?.type === EntityType.DataPlatform) {
const platform = aggregation.entity as DataPlatform; const platform = aggregation.entity as DataPlatform;
const displayName = platform.properties?.displayName || platform.info?.displayName || platform.name;
const truncatedDisplayName = displayName.length > 25 ? `${displayName.slice(0, 25)}...` : displayName;
return ( return (
<> <Tooltip title={displayName}>
{!!platform.properties?.logoUrl && ( {!!platform.properties?.logoUrl && (
<PreviewImage src={platform.properties?.logoUrl} alt={platform.name} /> <PreviewImage src={platform.properties?.logoUrl} alt={platform.name} />
)} )}
<span> <span>
{platform.properties?.displayName || platform.name} ({countText}) {truncatedDisplayName} ({countText})
</span> </span>
</> </Tooltip>
); );
} }
if (aggregation.entity?.type === EntityType.Container) { if (aggregation.entity?.type === EntityType.Container) {
const container = aggregation.entity as Container; const container = aggregation.entity as Container;
const displayName = entityRegistry.getDisplayName(EntityType.Container, container);
const truncatedDisplayName = displayName.length > 25 ? `${displayName.slice(0, 25)}...` : displayName;
return ( return (
<> <Tooltip title={displayName}>
{!!container.platform?.properties?.logoUrl && ( {!!container.platform?.properties?.logoUrl && (
<PreviewImage src={container.platform?.properties?.logoUrl} alt={container.properties?.name} /> <PreviewImage src={container.platform?.properties?.logoUrl} alt={container.properties?.name} />
)} )}
<span> <span>
{container.properties?.name} ({countText}) {truncatedDisplayName} ({countText})
</span> </span>
</> </Tooltip>
); );
} }
if (aggregation.entity?.type === EntityType.Domain) { if (aggregation.entity?.type === EntityType.Domain) {
const domain = aggregation.entity as Domain; const domain = aggregation.entity as Domain;
const displayName = entityRegistry.getDisplayName(EntityType.Domain, domain);
const truncatedDomainName = displayName.length > 25 ? `${displayName.slice(0, 25)}...` : displayName;
return ( return (
<> <Tooltip title={displayName}>
<DomainLink urn={domain.urn} name={entityRegistry.getDisplayName(EntityType.Domain, domain)} />( <DomainLink urn={domain.urn} name={truncatedDomainName} />({countText})
{countText}) </Tooltip>
</>
); );
} }
// Warning: Special casing for Sub-Types // Warning: Special casing for Sub-Types
if (field === 'typeNames') { if (field === 'typeNames') {
const subTypeDisplayName = capitalizeFirstLetter(aggregation.value); const displayName = capitalizeFirstLetter(aggregation.value) || '';
const truncatedDomainName = displayName.length > 25 ? `${displayName.slice(0, 25)}...` : displayName;
return ( return (
<> <Tooltip title={displayName}>
<span> <span>
{subTypeDisplayName} ({countText}) {truncatedDomainName} ({countText})
</span> </span>
</> </Tooltip>
); );
} }

View File

@ -1,11 +1,11 @@
import React, { useEffect, useState } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import { grey } from '@ant-design/colors'; import { grey } from '@ant-design/colors';
import { Alert, Button, Divider, message, Typography } from 'antd'; import { Alert, Button, Divider, message, Typography } from 'antd';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { ApolloError } from '@apollo/client'; import { ApolloError } from '@apollo/client';
import styled from 'styled-components'; import styled from 'styled-components';
import { ChromePicker } from 'react-color'; import { ChromePicker } from 'react-color';
import ColorHash from 'color-hash';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { useGetTagQuery } from '../../graphql/tag.generated'; import { useGetTagQuery } from '../../graphql/tag.generated';
import { EntityType, FacetMetadata, Maybe, Scalars } from '../../types.generated'; import { EntityType, FacetMetadata, Maybe, Scalars } from '../../types.generated';
@ -61,7 +61,6 @@ const TitleText = styled(Typography.Text)`
const ColorPicker = styled.div` const ColorPicker = styled.div`
position: relative; position: relative;
display: inline-block; display: inline-block;
margin-top: 1em;
`; `;
const ColorPickerButton = styled.div` const ColorPickerButton = styled.div`
@ -141,6 +140,12 @@ const OwnerButtonTitle = styled.span`
color: ${grey[10]}; color: ${grey[10]};
`; `;
const TagName = styled.div`
display: flex;
align-items: center;
justify-content: left;
`;
const { Paragraph } = Typography; const { Paragraph } = Typography;
type Props = { type Props = {
@ -152,6 +157,10 @@ type Props = {
}; };
}; };
const generateColor = new ColorHash({
saturation: 0.9,
});
/** /**
* Responsible for displaying metadata about a tag * Responsible for displaying metadata about a tag
*/ */
@ -166,7 +175,7 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
const description = data?.tag?.properties?.description || ''; const description = data?.tag?.properties?.description || '';
const [updatedDescription, setUpdatedDescription] = useState(''); const [updatedDescription, setUpdatedDescription] = useState('');
const hexColor = data?.tag?.properties?.colorHex || ''; const hexColor = data?.tag?.properties?.colorHex || generateColor.hex(urn);
const [displayColorPicker, setDisplayColorPicker] = useState(false); const [displayColorPicker, setDisplayColorPicker] = useState(false);
const [colorValue, setColorValue] = useState(''); const [colorValue, setColorValue] = useState('');
const ownersEmpty = !data?.tag?.ownership?.owners?.length; const ownersEmpty = !data?.tag?.ownership?.owners?.length;
@ -195,9 +204,8 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
const aggregations = facets && facets[0]?.aggregations; const aggregations = facets && facets[0]?.aggregations;
// Save Color Change // Save Color Change
const saveColor = async () => { const saveColor = useCallback(async () => {
if (displayColorPicker) { if (displayColorPicker) {
message.loading({ content: 'Saving...' });
try { try {
await setTagColorMutation({ await setTagColorMutation({
variables: { variables: {
@ -206,17 +214,42 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
}, },
}); });
message.destroy(); message.destroy();
message.success({ content: 'Color Updated', duration: 2 }); message.success({ content: 'Color Saved!', duration: 2 });
setDisplayColorPicker(false); setDisplayColorPicker(false);
} catch (e: unknown) { } catch (e: unknown) {
message.destroy(); message.destroy();
if (e instanceof Error) { if (e instanceof Error) {
message.error({ content: `Failed to update Color: \n ${e.message || ''}`, duration: 2 }); message.error({ content: `Failed to save tag color: \n ${e.message || ''}`, duration: 2 });
} }
} }
refetch?.(); refetch?.();
} }
}; }, [urn, colorValue, displayColorPicker, setTagColorMutation, setDisplayColorPicker, refetch]);
const colorPickerRef = useRef(null);
useEffect(() => {
/**
* Save Color if Clicked outside of the Color Picker
*/
function handleClickOutsideColorPicker(event) {
if (displayColorPicker) {
const { current }: any = colorPickerRef;
if (current) {
if (!current.contains(event.target)) {
setDisplayColorPicker(false);
saveColor();
}
}
}
}
// Bind the event listener
document.addEventListener('mousedown', handleClickOutsideColorPicker);
return () => {
// Unbind the event listener on clean up
document.removeEventListener('mousedown', handleClickOutsideColorPicker);
};
}, [colorPickerRef, displayColorPicker, saveColor]);
const handlePickerClick = () => { const handlePickerClick = () => {
setDisplayColorPicker(!displayColorPicker); setDisplayColorPicker(!displayColorPicker);
@ -271,15 +304,17 @@ export default function TagStyleEntity({ urn, useGetSearchResults = useWrappedSe
{/* Tag Title */} {/* Tag Title */}
<div> <div>
<TitleLabel>Tag</TitleLabel> <TitleLabel>Tag</TitleLabel>
<ColorPicker> <TagName>
<ColorPickerButton style={{ backgroundColor: colorValue }} onClick={handlePickerClick} /> <ColorPicker>
</ColorPicker> <ColorPickerButton style={{ backgroundColor: colorValue }} onClick={handlePickerClick} />
</ColorPicker>
<TitleText>{data?.tag?.properties?.name}</TitleText>
</TagName>
{displayColorPicker && ( {displayColorPicker && (
<ColorPickerPopOver> <ColorPickerPopOver ref={colorPickerRef}>
<ChromePicker color={colorValue} onChange={handleColorChange} /> <ChromePicker color={colorValue} onChange={handleColorChange} />
</ColorPickerPopOver> </ColorPickerPopOver>
)} )}
<TitleText>{data?.tag?.properties?.name}</TitleText>
</div> </div>
<Divider /> <Divider />
{/* Tag Description */} {/* Tag Description */}

View File

@ -16,13 +16,14 @@ export type Props = {
name: string; name: string;
closable?: boolean; closable?: boolean;
onClose?: (e: any) => void; onClose?: (e: any) => void;
tagStyle?: any | undefined;
}; };
export const DomainLink = ({ urn, name, closable, onClose }: Props): JSX.Element => { export const DomainLink = ({ urn, name, closable, onClose, tagStyle }: Props): JSX.Element => {
const entityRegistry = useEntityRegistry(); const entityRegistry = useEntityRegistry();
return ( return (
<DomainLinkContainer to={entityRegistry.getEntityUrl(EntityType.Domain, urn)}> <DomainLinkContainer to={entityRegistry.getEntityUrl(EntityType.Domain, urn)}>
<Tag closable={closable} onClose={onClose}> <Tag style={tagStyle} closable={closable} onClose={onClose}>
<span style={{ paddingRight: '4px' }}> <span style={{ paddingRight: '4px' }}>
{entityRegistry.getIcon(EntityType.Domain, 10, IconStyleType.ACCENT)} {entityRegistry.getIcon(EntityType.Domain, 10, IconStyleType.ACCENT)}
</span> </span>