From 02478e5b95c186332efdf1e5a7a65c7f8c61e980 Mon Sep 17 00:00:00 2001 From: "JUST.in DO IT" Date: Fri, 10 May 2024 10:28:57 -0700 Subject: [PATCH] feat(dashboard): Add metadata bar to the header (#27857) --- .../spec/fixtures/mockDashboardInfo.js | 13 +++++++++ .../components/Header/Header.test.tsx | 25 ++++++++++++++++ .../src/dashboard/components/Header/index.jsx | 29 +++++++++++++++++++ superset/dashboards/schemas.py | 2 ++ .../integration_tests/dashboards/api_tests.py | 2 ++ 5 files changed, 71 insertions(+) diff --git a/superset-frontend/spec/fixtures/mockDashboardInfo.js b/superset-frontend/spec/fixtures/mockDashboardInfo.js index 4fd599ea3b473..2f747fd07b557 100644 --- a/superset-frontend/spec/fixtures/mockDashboardInfo.js +++ b/superset-frontend/spec/fixtures/mockDashboardInfo.js @@ -31,6 +31,19 @@ export default { }, ], }, + changed_on_delta_humanized: '7 minutes ago', + changed_by: { + id: 3, + first_name: 'John', + last_name: 'Doe', + }, + created_on_delta_humanized: '10 days ago', + created_by: { + id: 2, + first_name: 'Kay', + last_name: 'Mon', + }, + owners: [{ first_name: 'John', last_name: 'Doe', id: 1 }], userId: 'mock_user_id', dash_edit_perm: true, dash_save_perm: true, diff --git a/superset-frontend/src/dashboard/components/Header/Header.test.tsx b/superset-frontend/src/dashboard/components/Header/Header.test.tsx index 2df5fa8318912..9160be6ad82ce 100644 --- a/superset-frontend/src/dashboard/components/Header/Header.test.tsx +++ b/superset-frontend/src/dashboard/components/Header/Header.test.tsx @@ -22,6 +22,7 @@ import userEvent from '@testing-library/user-event'; import fetchMock from 'fetch-mock'; import { getExtensionsRegistry } from '@superset-ui/core'; import setupExtensions from 'src/setup/setupExtensions'; +import getOwnerName from 'src/utils/getOwnerName'; import { HeaderProps } from './types'; import Header from '.'; @@ -44,6 +45,19 @@ const createProps = () => ({ ], }, }, + changed_on_delta_humanized: '7 minutes ago', + changed_by: { + id: 3, + first_name: 'John', + last_name: 'Doe', + }, + created_on_delta_humanized: '10 days ago', + created_by: { + id: 2, + first_name: 'Kay', + last_name: 'Mon', + }, + owners: [{ first_name: 'John', last_name: 'Doe', id: 1 }], }, user: { createdOn: '2021-04-27T18:12:38.952304', @@ -187,6 +201,17 @@ test('should publish', () => { expect(mockedProps.savePublished).toHaveBeenCalledTimes(1); }); +test('should render metadata', () => { + const mockedProps = createProps(); + setup(mockedProps); + expect( + screen.getByText(getOwnerName(mockedProps.dashboardInfo.created_by)), + ).toBeInTheDocument(); + expect( + screen.getByText(mockedProps.dashboardInfo.changed_on_delta_humanized), + ).toBeInTheDocument(); +}); + test('should render the "Undo" action as disabled', () => { setup(editableProps); expect(screen.getByTestId('undo-action').parentElement).toBeDisabled(); diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx index f4a864b83540f..bce3e60d6f8f4 100644 --- a/superset-frontend/src/dashboard/components/Header/index.jsx +++ b/superset-frontend/src/dashboard/components/Header/index.jsx @@ -46,6 +46,7 @@ import PublishedStatus from 'src/dashboard/components/PublishedStatus'; import UndoRedoKeyListeners from 'src/dashboard/components/UndoRedoKeyListeners'; import PropertiesModal from 'src/dashboard/components/PropertiesModal'; import { chartPropShape } from 'src/dashboard/util/propShapes'; +import getOwnerName from 'src/utils/getOwnerName'; import { UNDO_LIMIT, SAVE_TYPE_OVERWRITE, @@ -55,6 +56,7 @@ import setPeriodicRunner, { stopPeriodicRender, } from 'src/dashboard/util/setPeriodicRunner'; import { PageHeaderWithActions } from 'src/components/PageHeaderWithActions'; +import MetadataBar, { MetadataType } from 'src/components/MetadataBar'; import DashboardEmbedModal from '../EmbeddedModal'; import OverwriteConfirm from '../OverwriteConfirm'; @@ -435,6 +437,27 @@ class Header extends React.PureComponent { this.setState({ showingEmbedModal: false }); }; + getMetadataItems = () => { + const { dashboardInfo } = this.props; + return [ + { + type: MetadataType.LastModified, + value: dashboardInfo.changed_on_delta_humanized, + modifiedBy: + getOwnerName(dashboardInfo.changed_by) || t('Not available'), + }, + { + type: MetadataType.Owner, + createdBy: getOwnerName(dashboardInfo.created_by) || t('Not available'), + owners: + dashboardInfo.owners.length > 0 + ? dashboardInfo.owners.map(getOwnerName) + : t('None'), + createdOn: dashboardInfo.created_on_delta_humanized, + }, + ]; + }; + render() { const { dashboardTitle, @@ -535,6 +558,12 @@ class Header extends React.PureComponent { visible={!editMode} /> ), + !editMode && ( + + ), ]} rightPanelAdditionalItems={
diff --git a/superset/dashboards/schemas.py b/superset/dashboards/schemas.py index 29b13ae7b860f..6a6debe397541 100644 --- a/superset/dashboards/schemas.py +++ b/superset/dashboards/schemas.py @@ -190,11 +190,13 @@ class DashboardGetResponseSchema(Schema): changed_by_name = fields.String() changed_by = fields.Nested(UserSchema(exclude=["username"])) changed_on = fields.DateTime() + created_by = fields.Nested(UserSchema(exclude=["username"])) charts = fields.List(fields.String(metadata={"description": charts_description})) owners = fields.List(fields.Nested(UserSchema(exclude=["username"]))) roles = fields.List(fields.Nested(RolesSchema)) tags = fields.Nested(TagSchema, many=True) changed_on_humanized = fields.String(data_key="changed_on_delta_humanized") + created_on_humanized = fields.String(data_key="created_on_delta_humanized") is_managed_externally = fields.Boolean(allow_none=True, dump_default=False) # pylint: disable=unused-argument diff --git a/tests/integration_tests/dashboards/api_tests.py b/tests/integration_tests/dashboards/api_tests.py index 949b6551193c6..328fb5774eecc 100644 --- a/tests/integration_tests/dashboards/api_tests.py +++ b/tests/integration_tests/dashboards/api_tests.py @@ -423,11 +423,13 @@ def test_get_dashboard(self): data = json.loads(rv.data.decode("utf-8")) self.assertIn("changed_on", data["result"]) self.assertIn("changed_on_delta_humanized", data["result"]) + self.assertIn("created_on_delta_humanized", data["result"]) for key, value in data["result"].items(): # We can't assert timestamp values if key not in ( "changed_on", "changed_on_delta_humanized", + "created_on_delta_humanized", ): self.assertEqual(value, expected_result[key]) # rollback changes