mirror of https://github.com/apache/superset.git
feat: add extension point for workspace home page (#21033)
* 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
This commit is contained in:
parent
d817a1dc87
commit
83dd85166f
|
@ -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;
|
||||
}>;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
@ -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`
|
||||
|
@ -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;
|
||||
|
@ -161,6 +168,7 @@ interface CardProps {
|
|||
|
||||
function ListViewCard({
|
||||
title,
|
||||
subtitle,
|
||||
url,
|
||||
linkComponent,
|
||||
titleRight,
|
||||
|
@ -245,6 +253,8 @@ function ListViewCard({
|
|||
<AntdCard.Meta
|
||||
title={
|
||||
<TitleContainer>
|
||||
{subtitle || null}
|
||||
<div className="titleRow">
|
||||
<Tooltip title={title}>
|
||||
<TitleLink>
|
||||
<Link to={url!}>
|
||||
|
@ -264,6 +274,7 @@ function ListViewCard({
|
|||
<div className="card-actions" data-test="card-actions">
|
||||
{actions}
|
||||
</div>
|
||||
</div>
|
||||
</TitleContainer>
|
||||
}
|
||||
description={description}
|
||||
|
|
|
@ -57,4 +57,5 @@ const StyledGroup = styled(AntdRadio.Group)`
|
|||
|
||||
export const Radio = Object.assign(StyledRadio, {
|
||||
Group: StyledGroup,
|
||||
Button: AntdRadio.Button,
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
@ -282,7 +286,11 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
!activityData?.Examples && !activityData?.Viewed;
|
||||
return (
|
||||
<WelcomeContainer>
|
||||
{WelcomeMessageExtension && <WelcomeMessageExtension />}
|
||||
{WelcomeTopExtension && <WelcomeTopExtension />}
|
||||
{WelcomeMainExtension && <WelcomeMainExtension />}
|
||||
{(!WelcomeTopExtension || !WelcomeMainExtension) && (
|
||||
<>
|
||||
<WelcomeNav>
|
||||
<h1 className="welcome-header">Home</h1>
|
||||
{isFeatureEnabled(FeatureFlag.THUMBNAILS) ? (
|
||||
|
@ -292,7 +300,12 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
</div>
|
||||
) : null}
|
||||
</WelcomeNav>
|
||||
<Collapse activeKey={activeState} onChange={handleCollapse} ghost bigger>
|
||||
<Collapse
|
||||
activeKey={activeState}
|
||||
onChange={handleCollapse}
|
||||
ghost
|
||||
bigger
|
||||
>
|
||||
<Collapse.Panel header={t('Recents')} key="1">
|
||||
{activityData &&
|
||||
(activityData.Viewed ||
|
||||
|
@ -347,6 +360,8 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
)}
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</>
|
||||
)}
|
||||
</WelcomeContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -114,16 +114,20 @@ class ChartRestApi(BaseSupersetModelRestApi):
|
|||
"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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -762,7 +763,19 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixin):
|
|||
"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()
|
||||
|
||||
|
|
|
@ -525,7 +525,9 @@ class TestSavedQueryApi(SupersetTestCase):
|
|||
"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():
|
||||
if key not in ("changed_on_delta_humanized",):
|
||||
assert value == expected_result[key]
|
||||
|
||||
def test_get_saved_query_not_found(self):
|
||||
|
|
Loading…
Reference in New Issue