Skip to content

Commit

Permalink
feat: add extension point for workspace home page (#21033)
Browse files Browse the repository at this point in the history
* updates to allow insertion of workspace home sidescroll/table UI

* fix types

* fix User type import

* add welcome message to ui registry

* add extra fields to individual chart/query GET results (for workspace home required info)

* update list view card to support a subtitle

* add id to individual chart fetch

* update chart api test

* another test fix

* fix saved query test

* update extension types + insert point

* fix typing

* fix type name
  • Loading branch information
riahk authored Aug 15, 2022
1 parent d817a1d commit 83dd851
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ export type Extensions = Partial<{
'embedded.documentation.url': string;
'dashboard.nav.right': React.ComponentType;
'navbar.right': React.ComponentType;
'welcome.message': React.ComponentType;
'welcome.banner': React.ComponentType;
'welcome.main.replacement': React.ComponentType;
}>;

/**
Expand Down
49 changes: 30 additions & 19 deletions superset-frontend/src/components/ListViewCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const Cover = styled.div`
const TitleContainer = styled.div`
display: flex;
justify-content: flex-start;
flex-direction: row;
flex-direction: column;
.card-actions {
margin-left: auto;
Expand All @@ -82,6 +82,12 @@ const TitleContainer = styled.div`
align-items: center;
}
}
.titleRow {
display: flex;
justify-content: flex-start;
flex-direction: row;
}
`;

const TitleLink = styled.span`
Expand Down Expand Up @@ -141,6 +147,7 @@ const AnchorLink: React.FC<LinkProps> = ({ to, children }) => (

interface CardProps {
title?: React.ReactNode;
subtitle?: React.ReactNode;
url?: string;
linkComponent?: React.ComponentType<LinkProps>;
imgURL?: string;
Expand All @@ -161,6 +168,7 @@ interface CardProps {

function ListViewCard({
title,
subtitle,
url,
linkComponent,
titleRight,
Expand Down Expand Up @@ -245,24 +253,27 @@ function ListViewCard({
<AntdCard.Meta
title={
<TitleContainer>
<Tooltip title={title}>
<TitleLink>
<Link to={url!}>
{certifiedBy && (
<>
<CertifiedBadge
certifiedBy={certifiedBy}
details={certificationDetails}
/>{' '}
</>
)}
{title}
</Link>
</TitleLink>
</Tooltip>
{titleRight && <TitleRight>{titleRight}</TitleRight>}
<div className="card-actions" data-test="card-actions">
{actions}
{subtitle || null}
<div className="titleRow">
<Tooltip title={title}>
<TitleLink>
<Link to={url!}>
{certifiedBy && (
<>
<CertifiedBadge
certifiedBy={certifiedBy}
details={certificationDetails}
/>{' '}
</>
)}
{title}
</Link>
</TitleLink>
</Tooltip>
{titleRight && <TitleRight>{titleRight}</TitleRight>}
<div className="card-actions" data-test="card-actions">
{actions}
</div>
</div>
</TitleContainer>
}
Expand Down
1 change: 1 addition & 0 deletions superset-frontend/src/components/Radio/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,5 @@ const StyledGroup = styled(AntdRadio.Group)`

export const Radio = Object.assign(StyledRadio, {
Group: StyledGroup,
Button: AntdRadio.Button,
});
143 changes: 79 additions & 64 deletions superset-frontend/src/views/CRUD/welcome/Welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
setItem(LocalStorageKeys.homepage_collapse_state, state);
};

const WelcomeMessageExtension = extensionsRegistry.get('welcome.message');
const WelcomeTopExtension = extensionsRegistry.get('welcome.banner');
const WelcomeMainExtension = extensionsRegistry.get(
'welcome.main.replacement',
);

useEffect(() => {
const activeTab = getItem(LocalStorageKeys.homepage_activity_filter, null);
Expand Down Expand Up @@ -282,71 +286,82 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
!activityData?.Examples && !activityData?.Viewed;
return (
<WelcomeContainer>
{WelcomeMessageExtension && <WelcomeMessageExtension />}
{WelcomeTopExtension && <WelcomeTopExtension />}
<WelcomeNav>
<h1 className="welcome-header">Home</h1>
{isFeatureEnabled(FeatureFlag.THUMBNAILS) ? (
<div className="switch">
<AntdSwitch checked={checked} onChange={handleToggle} />
<span>Thumbnails</span>
</div>
) : null}
</WelcomeNav>
<Collapse activeKey={activeState} onChange={handleCollapse} ghost bigger>
<Collapse.Panel header={t('Recents')} key="1">
{activityData &&
(activityData.Viewed ||
activityData.Examples ||
activityData.Created) &&
activeChild !== 'Loading' ? (
<ActivityTable
user={{ userId: user.userId! }} // user is definitely not a guest user on this page
activeChild={activeChild}
setActiveChild={setActiveChild}
activityData={activityData}
loadedCount={loadedCount}
/>
) : (
<LoadingCards />
)}
</Collapse.Panel>
<Collapse.Panel header={t('Dashboards')} key="2">
{!dashboardData || isRecentActivityLoading ? (
<LoadingCards cover={checked} />
) : (
<DashboardTable
user={user}
mine={dashboardData}
showThumbnails={checked}
examples={activityData?.Examples}
/>
)}
</Collapse.Panel>
<Collapse.Panel header={t('Charts')} key="3">
{!chartData || isRecentActivityLoading ? (
<LoadingCards cover={checked} />
) : (
<ChartTable
showThumbnails={checked}
user={user}
mine={chartData}
examples={activityData?.Examples}
/>
)}
</Collapse.Panel>
<Collapse.Panel header={t('Saved queries')} key="4">
{!queryData ? (
<LoadingCards cover={checked} />
) : (
<SavedQueries
showThumbnails={checked}
user={user}
mine={queryData}
featureFlag={isFeatureEnabled(FeatureFlag.THUMBNAILS)}
/>
)}
</Collapse.Panel>
</Collapse>
{WelcomeMainExtension && <WelcomeMainExtension />}
{(!WelcomeTopExtension || !WelcomeMainExtension) && (
<>
<WelcomeNav>
<h1 className="welcome-header">Home</h1>
{isFeatureEnabled(FeatureFlag.THUMBNAILS) ? (
<div className="switch">
<AntdSwitch checked={checked} onChange={handleToggle} />
<span>Thumbnails</span>
</div>
) : null}
</WelcomeNav>
<Collapse
activeKey={activeState}
onChange={handleCollapse}
ghost
bigger
>
<Collapse.Panel header={t('Recents')} key="1">
{activityData &&
(activityData.Viewed ||
activityData.Examples ||
activityData.Created) &&
activeChild !== 'Loading' ? (
<ActivityTable
user={{ userId: user.userId! }} // user is definitely not a guest user on this page
activeChild={activeChild}
setActiveChild={setActiveChild}
activityData={activityData}
loadedCount={loadedCount}
/>
) : (
<LoadingCards />
)}
</Collapse.Panel>
<Collapse.Panel header={t('Dashboards')} key="2">
{!dashboardData || isRecentActivityLoading ? (
<LoadingCards cover={checked} />
) : (
<DashboardTable
user={user}
mine={dashboardData}
showThumbnails={checked}
examples={activityData?.Examples}
/>
)}
</Collapse.Panel>
<Collapse.Panel header={t('Charts')} key="3">
{!chartData || isRecentActivityLoading ? (
<LoadingCards cover={checked} />
) : (
<ChartTable
showThumbnails={checked}
user={user}
mine={chartData}
examples={activityData?.Examples}
/>
)}
</Collapse.Panel>
<Collapse.Panel header={t('Saved queries')} key="4">
{!queryData ? (
<LoadingCards cover={checked} />
) : (
<SavedQueries
showThumbnails={checked}
user={user}
mine={queryData}
featureFlag={isFeatureEnabled(FeatureFlag.THUMBNAILS)}
/>
)}
</Collapse.Panel>
</Collapse>
</>
)}
</WelcomeContainer>
);
}
Expand Down
4 changes: 4 additions & 0 deletions superset/charts/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,20 @@ def ensure_thumbnails_enabled(self) -> Optional[Response]:
"cache_timeout",
"certified_by",
"certification_details",
"changed_on_delta_humanized",
"dashboards.dashboard_title",
"dashboards.id",
"dashboards.json_metadata",
"description",
"id",
"owners.first_name",
"owners.id",
"owners.last_name",
"owners.username",
"params",
"slice_name",
"thumbnail_url",
"url",
"viz_type",
"query_context",
"is_managed_externally",
Expand Down
1 change: 1 addition & 0 deletions superset/queries/saved_queries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class SavedQueryRestApi(BaseSupersetModelRestApi):
base_filters = [["id", SavedQueryFilter, lambda: []]]

show_columns = [
"changed_on_delta_humanized",
"created_by.first_name",
"created_by.id",
"created_by.last_name",
Expand Down
15 changes: 14 additions & 1 deletion tests/integration_tests/charts/api_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# isort:skip_file
"""Unit tests for Superset"""
import json
import logging
from io import BytesIO
from zipfile import is_zipfile, ZipFile

Expand Down Expand Up @@ -762,7 +763,19 @@ def test_get_chart(self):
"is_managed_externally": False,
}
data = json.loads(rv.data.decode("utf-8"))
self.assertEqual(data["result"], expected_result)
self.assertIn("changed_on_delta_humanized", data["result"])
self.assertIn("id", data["result"])
self.assertIn("thumbnail_url", data["result"])
self.assertIn("url", data["result"])
for key, value in data["result"].items():
# We can't assert timestamp values or id/urls
if key not in (
"changed_on_delta_humanized",
"id",
"thumbnail_url",
"url",
):
self.assertEqual(value, expected_result[key])
db.session.delete(chart)
db.session.commit()

Expand Down
4 changes: 3 additions & 1 deletion tests/integration_tests/queries/saved_queries/api_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,10 @@ def test_get_saved_query(self):
"label": "label1",
}
data = json.loads(rv.data.decode("utf-8"))
self.assertIn("changed_on_delta_humanized", data["result"])
for key, value in data["result"].items():
assert value == expected_result[key]
if key not in ("changed_on_delta_humanized",):
assert value == expected_result[key]

def test_get_saved_query_not_found(self):
"""
Expand Down

0 comments on commit 83dd851

Please sign in to comment.