Skip to content

Commit

Permalink
feat(datasets): Populate Usage tab in Edit Dataset view (apache#22670)
Browse files Browse the repository at this point in the history
  • Loading branch information
codyml authored and PawankumarES committed Feb 13, 2023
1 parent 17bb894 commit 3fc21e0
Show file tree
Hide file tree
Showing 7 changed files with 901 additions and 7 deletions.
55 changes: 55 additions & 0 deletions superset-frontend/spec/fixtures/mockCharts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/

export interface ChartListChart {
id: number;
slice_name: string;
url: string;
last_saved_at: null | string;
last_saved_by: null | { id: number; first_name: string; last_name: string };
owners: {
id: number;
first_name: string;
last_name: string;
username: string;
}[];
dashboards: { id: number; dashboard_title: string }[];
}

const CHART_ID = 1;
const MOCK_CHART: ChartListChart = {
id: CHART_ID,
slice_name: 'Sample chart',
url: `/explore/?slice_id=${CHART_ID}`,
last_saved_at: null,
dashboards: [],
last_saved_by: null,
owners: [],
};

/**
* Get mock charts as would be returned by the /api/v1/chart list endpoint.
*/
export const getMockChart = (
overrides: Partial<ChartListChart> = {},
): ChartListChart => ({
...MOCK_CHART,
...(overrides.id ? { url: `/explore/?slice_id=${overrides.id}` } : null),
...overrides,
});
160 changes: 160 additions & 0 deletions superset-frontend/src/components/TruncatedList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 React, { ReactNode, useMemo, useRef } from 'react';
import { styled, t } from '@superset-ui/core';
import { useTruncation } from 'src/hooks/useTruncation';
import { Tooltip } from '../Tooltip';

export type TruncatedListProps<ListItemType> = {
/**
* Array of input items of type `ListItemType`.
*/
items: ListItemType[];

/**
* Renderer for items not overflowed into the tooltip.
* Required if `ListItemType` is not renderable by React.
*/
renderVisibleItem?: (item: ListItemType) => ReactNode;

/**
* Renderer for items that are overflowed into the tooltip.
* Required if `ListItemType` is not renderable by React.
*/
renderTooltipItem?: (item: ListItemType) => ReactNode;

/**
* Returns the React key for an item.
*/
getKey?: (item: ListItemType) => React.Key;

/**
* The max number of links that should appear in the tooltip.
*/
maxLinks?: number;
};

const StyledTruncatedList = styled.div`
& > span {
width: 100%;
display: flex;
.ant-tooltip-open {
display: inline;
}
}
`;

const StyledVisibleItems = styled.span`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
width: 100%;
vertical-align: bottom;
`;

const StyledVisibleItem = styled.span`
&:not(:last-child)::after {
content: ', ';
}
`;

const StyledTooltipItem = styled.div`
.link {
color: ${({ theme }) => theme.colors.grayscale.light5};
display: block;
text-decoration: underline;
}
`;

const StyledPlus = styled.span`
${({ theme }) => `
cursor: pointer;
color: ${theme.colors.primary.dark1};
font-weight: ${theme.typography.weights.normal};
`}
`;

export default function TruncatedList<ListItemType>({
items,
renderVisibleItem = item => item,
renderTooltipItem = item => item,
getKey = item => item as unknown as React.Key,
maxLinks = 20,
}: TruncatedListProps<ListItemType>) {
const itemsNotInTooltipRef = useRef<HTMLDivElement>(null);
const plusRef = useRef<HTMLDivElement>(null);
const [elementsTruncated, hasHiddenElements] = useTruncation(
itemsNotInTooltipRef,
plusRef,
) as [number, boolean];

const nMoreItems = useMemo(
() => (items.length > maxLinks ? items.length - maxLinks : undefined),
[items, maxLinks],
);

const itemsNotInTooltip = useMemo(
() => (
<StyledVisibleItems ref={itemsNotInTooltipRef} data-test="crosslinks">
{items.map(item => (
<StyledVisibleItem key={getKey(item)}>
{renderVisibleItem(item)}
</StyledVisibleItem>
))}
</StyledVisibleItems>
),
[getKey, items, renderVisibleItem],
);

const itemsInTooltip = useMemo(
() =>
items
.slice(0, maxLinks)
.map(item => (
<StyledTooltipItem key={getKey(item)}>
{renderTooltipItem(item)}
</StyledTooltipItem>
)),
[getKey, items, maxLinks, renderTooltipItem],
);

return (
<StyledTruncatedList>
<Tooltip
placement="top"
title={
elementsTruncated ? (
<>
{itemsInTooltip}
{nMoreItems && <span>{t('+ %s more', nMoreItems)}</span>}
</>
) : null
}
>
{itemsNotInTooltip}
{hasHiddenElements && (
<StyledPlus ref={plusRef}>+{elementsTruncated}</StyledPlus>
)}
</Tooltip>
</StyledTruncatedList>
);
}
7 changes: 1 addition & 6 deletions superset-frontend/src/pages/ChartList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import { dangerouslyGetItemDoNotUse } from 'src/utils/localStorageHelpers';
import withToasts from 'src/components/MessageToasts/withToasts';
import PropertiesModal from 'src/explore/components/PropertiesModal';
import ImportModelsModal from 'src/components/ImportModal/index';
import Chart from 'src/types/Chart';
import Chart, { ChartLinkedDashboard } from 'src/types/Chart';
import { Tooltip } from 'src/components/Tooltip';
import Icons from 'src/components/Icons';
import { nativeFilterGate } from 'src/dashboard/components/nativeFilters/utils';
Expand Down Expand Up @@ -148,11 +148,6 @@ interface ChartListProps {
};
}

type ChartLinkedDashboard = {
id: number;
dashboard_title: string;
};

const Actions = styled.div`
color: ${({ theme }) => theme.colors.grayscale.base};
`;
Expand Down
14 changes: 14 additions & 0 deletions superset-frontend/src/types/Chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
import { QueryFormData } from '@superset-ui/core';
import Owner from './Owner';

export type ChartLinkedDashboard = {
id: number;
dashboard_title: string;
};

export interface Chart {
id: number;
url: string;
Expand All @@ -39,11 +44,20 @@ export interface Chart {
cache_timeout: number | null;
thumbnail_url?: string;
owners?: Owner[];
last_saved_at?: string;
last_saved_by?: {
id: number;
first_name: string;
last_name: string;
};
datasource_name_text?: string;
form_data: {
viz_type: string;
};
is_managed_externally: boolean;

// TODO: Update API spec to describe `dashboards` key
dashboards: ChartLinkedDashboard[];
}

export type Slice = {
Expand Down
Loading

0 comments on commit 3fc21e0

Please sign in to comment.