feat(viz-gallery): add 'feature' tag and fuzzy search weighting (#18662)

* feat(viz-gallery): add 'feature' tag and fuzzy search weighting

* add search weight

* Update superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* some improvements

* take metadata out

* use chartLabel enum to unify

* add test chart

* fix cache

* Resolving TS Lint issue

* Appeasing the linter

* Removing one example implementation

* Removing another example label implementation

* Removing the third example label implementation

Co-authored-by: Evan Rusackas <evan@preset.io>
This commit is contained in:
Stephen Liu 2022-03-11 16:49:10 +08:00 committed by GitHub
parent 3a78165d13
commit 7524e1e3c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 8 deletions

View File

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import { Behavior } from '../types/Base'; import { Behavior, ChartLabel } from '../types/Base';
interface LookupTable { interface LookupTable {
[key: string]: boolean; [key: string]: boolean;
@ -40,10 +40,13 @@ export interface ChartMetadataConfig {
thumbnail: string; thumbnail: string;
useLegacyApi?: boolean; useLegacyApi?: boolean;
behaviors?: Behavior[]; behaviors?: Behavior[];
deprecated?: boolean;
exampleGallery?: ExampleImage[]; exampleGallery?: ExampleImage[];
tags?: string[]; tags?: string[];
category?: string | null; category?: string | null;
label?: {
name?: ChartLabel;
description?: string;
} | null;
} }
export default class ChartMetadata { export default class ChartMetadata {
@ -71,14 +74,17 @@ export default class ChartMetadata {
enableNoResults: boolean; enableNoResults: boolean;
deprecated: boolean;
exampleGallery: ExampleImage[]; exampleGallery: ExampleImage[];
tags: string[]; tags: string[];
category: string | null; category: string | null;
label?: {
name?: ChartLabel;
description?: string;
} | null;
constructor(config: ChartMetadataConfig) { constructor(config: ChartMetadataConfig) {
const { const {
name, name,
@ -92,10 +98,10 @@ export default class ChartMetadata {
behaviors = [], behaviors = [],
datasourceCount = 1, datasourceCount = 1,
enableNoResults = true, enableNoResults = true,
deprecated = false,
exampleGallery = [], exampleGallery = [],
tags = [], tags = [],
category = null, category = null,
label = null,
} = config; } = config;
this.name = name; this.name = name;
@ -118,10 +124,10 @@ export default class ChartMetadata {
this.behaviors = behaviors; this.behaviors = behaviors;
this.datasourceCount = datasourceCount; this.datasourceCount = datasourceCount;
this.enableNoResults = enableNoResults; this.enableNoResults = enableNoResults;
this.deprecated = deprecated;
this.exampleGallery = exampleGallery; this.exampleGallery = exampleGallery;
this.tags = tags; this.tags = tags;
this.category = category; this.category = category;
this.label = label;
} }
canBeAnnotationType(type: string): boolean { canBeAnnotationType(type: string): boolean {

View File

@ -52,4 +52,22 @@ export interface PlainObject {
[key: string]: any; [key: string]: any;
} }
export enum ChartLabel {
VERIFIED = 'VERIFIED',
DEPRECATED = 'DEPRECATED',
FEATURED = 'FEATURED',
}
export const ChartLabelWeight = {
[ChartLabel.DEPRECATED]: {
weight: -0.1,
},
[ChartLabel.VERIFIED]: {
weight: 0.2,
},
[ChartLabel.FEATURED]: {
weight: 0.1,
},
};
export default {}; export default {};

View File

@ -33,8 +33,11 @@ import {
ChartMetadata, ChartMetadata,
SupersetTheme, SupersetTheme,
useTheme, useTheme,
ChartLabel,
ChartLabelWeight,
} from '@superset-ui/core'; } from '@superset-ui/core';
import { AntdCollapse } from 'src/components'; import { AntdCollapse } from 'src/components';
import { Tooltip } from 'src/components/Tooltip';
import { Input } from 'src/components/Input'; import { Input } from 'src/components/Input';
import Label from 'src/components/Label'; import Label from 'src/components/Label';
import { usePluginContext } from 'src/components/DynamicPlugins'; import { usePluginContext } from 'src/components/DynamicPlugins';
@ -310,6 +313,7 @@ const Examples = styled.div`
const thumbnailContainerCss = (theme: SupersetTheme) => css` const thumbnailContainerCss = (theme: SupersetTheme) => css`
cursor: pointer; cursor: pointer;
width: ${theme.gridUnit * THUMBNAIL_GRID_UNITS}px; width: ${theme.gridUnit * THUMBNAIL_GRID_UNITS}px;
position: relative;
img { img {
min-width: ${theme.gridUnit * THUMBNAIL_GRID_UNITS}px; min-width: ${theme.gridUnit * THUMBNAIL_GRID_UNITS}px;
@ -333,6 +337,38 @@ const thumbnailContainerCss = (theme: SupersetTheme) => css`
} }
`; `;
const HighlightLabel = styled.div`
${({ theme }) => `
border: 1px solid ${theme.colors.primary.dark1};
box-sizing: border-box;
border-radius: ${theme.gridUnit}px;
background: ${theme.colors.grayscale.light5};
line-height: ${theme.gridUnit * 2.5}px;
color: ${theme.colors.primary.dark1};
font-size: ${theme.typography.sizes.s}px;
font-weight: ${theme.typography.weights.bold};
text-align: center;
padding: ${theme.gridUnit * 0.5}px ${theme.gridUnit}px;
text-transform: uppercase;
cursor: pointer;
div {
transform: scale(0.83,0.83);
}
`}
`;
const ThumbnailLabelWrapper = styled.div`
position: absolute;
right: ${({ theme }) => theme.gridUnit}px;
top: ${({ theme }) => theme.gridUnit * 19}px;
`;
const TitleLabelWrapper = styled.div`
display: inline-block !important;
margin-left: ${({ theme }) => theme.gridUnit * 2}px;
`;
function vizSortFactor(entry: VizEntry) { function vizSortFactor(entry: VizEntry) {
if (typesWithDefaultOrder.has(entry.key)) { if (typesWithDefaultOrder.has(entry.key)) {
return DEFAULT_ORDER.indexOf(entry.key); return DEFAULT_ORDER.indexOf(entry.key);
@ -378,6 +414,13 @@ const Thumbnail: React.FC<ThumbnailProps> = ({
> >
{type.name} {type.name}
</div> </div>
{type.label?.name && (
<ThumbnailLabelWrapper>
<HighlightLabel>
<div>{t(type.label?.name)}</div>
</HighlightLabel>
</ThumbnailLabelWrapper>
)}
</div> </div>
); );
}; };
@ -460,7 +503,8 @@ export default function VizTypeGallery(props: VizTypeGalleryProps) {
.map(([key, value]) => ({ key, value })) .map(([key, value]) => ({ key, value }))
.filter( .filter(
({ value }) => ({ value }) =>
nativeFilterGate(value.behaviors || []) && !value.deprecated, nativeFilterGate(value.behaviors || []) &&
value.label?.name !== ChartLabel.DEPRECATED,
); );
result.sort((a, b) => vizSortFactor(a) - vizSortFactor(b)); result.sort((a, b) => vizSortFactor(a) - vizSortFactor(b));
return result; return result;
@ -545,7 +589,18 @@ export default function VizTypeGallery(props: VizTypeGalleryProps) {
if (searchInputValue.trim() === '') { if (searchInputValue.trim() === '') {
return []; return [];
} }
return fuse.search(searchInputValue).map(result => result.item); return fuse
.search(searchInputValue)
.map(result => result.item)
.sort((a, b) => {
const aName = a.value?.label?.name;
const bName = b.value?.label?.name;
const aOrder =
aName && ChartLabelWeight[aName] ? ChartLabelWeight[aName].weight : 0;
const bOrder =
bName && ChartLabelWeight[bName] ? ChartLabelWeight[bName].weight : 0;
return bOrder - aOrder;
});
}, [searchInputValue, fuse]); }, [searchInputValue, fuse]);
const focusSearch = useCallback(() => { const focusSearch = useCallback(() => {
@ -739,9 +794,23 @@ export default function VizTypeGallery(props: VizTypeGalleryProps) {
<SectionTitle <SectionTitle
css={css` css={css`
grid-area: viz-name; grid-area: viz-name;
position: relative;
`} `}
> >
{selectedVizMetadata?.name} {selectedVizMetadata?.name}
{selectedVizMetadata?.label?.name && (
<Tooltip
id="viz-badge-tooltip"
placement="top"
title={selectedVizMetadata.label?.description}
>
<TitleLabelWrapper>
<HighlightLabel>
<div>{t(selectedVizMetadata.label?.name)}</div>
</HighlightLabel>
</TitleLabelWrapper>
</Tooltip>
)}
</SectionTitle> </SectionTitle>
<TagsWrapper> <TagsWrapper>
{selectedVizMetadata?.tags.map(tag => ( {selectedVizMetadata?.tags.map(tag => (