diff --git a/superset-frontend/src/components/MetadataBar/ContentType.ts b/superset-frontend/src/components/MetadataBar/ContentType.ts index b4fcb103f..6f88d556a 100644 --- a/superset-frontend/src/components/MetadataBar/ContentType.ts +++ b/superset-frontend/src/components/MetadataBar/ContentType.ts @@ -51,7 +51,7 @@ export type LastModified = { export type Owner = { type: MetadataType.Owner; createdBy: string; - owners?: string[]; + owners?: string[] | string; createdOn: string; onClick?: (type: string) => void; }; diff --git a/superset-frontend/src/components/MetadataBar/MetadataBar.tsx b/superset-frontend/src/components/MetadataBar/MetadataBar.tsx index 2a5826480..3503021f8 100644 --- a/superset-frontend/src/components/MetadataBar/MetadataBar.tsx +++ b/superset-frontend/src/components/MetadataBar/MetadataBar.tsx @@ -23,27 +23,18 @@ import { styled } from '@superset-ui/core'; import { Tooltip, TooltipPlacement } from 'src/components/Tooltip'; import { ContentType } from './ContentType'; import { config } from './ContentConfig'; - -export const MIN_NUMBER_ITEMS = 2; -export const MAX_NUMBER_ITEMS = 6; - -const HORIZONTAL_PADDING = 12; -const VERTICAL_PADDING = 8; -const ICON_PADDING = 8; -const SPACE_BETWEEN_ITEMS = 16; -const ICON_WIDTH = 16; -const TEXT_MIN_WIDTH = 70; -const TEXT_MAX_WIDTH = 150; -const ORDER = { - dashboards: 0, - table: 1, - sql: 2, - rows: 3, - tags: 4, - description: 5, - owner: 6, - lastModified: 7, -}; +import { + HORIZONTAL_PADDING, + ICON_PADDING, + ICON_WIDTH, + VERTICAL_PADDING, + TEXT_MIN_WIDTH, + TEXT_MAX_WIDTH, + SPACE_BETWEEN_ITEMS, + ORDER, + MIN_NUMBER_ITEMS, + MAX_NUMBER_ITEMS, +} from './constants'; const Bar = styled.div<{ count: number }>` ${({ theme, count }) => ` diff --git a/superset-frontend/src/components/MetadataBar/constants.ts b/superset-frontend/src/components/MetadataBar/constants.ts new file mode 100644 index 000000000..9114c95dd --- /dev/null +++ b/superset-frontend/src/components/MetadataBar/constants.ts @@ -0,0 +1,39 @@ +/** + * 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 const MIN_NUMBER_ITEMS = 2; +export const MAX_NUMBER_ITEMS = 6; + +export const HORIZONTAL_PADDING = 12; +export const VERTICAL_PADDING = 8; +export const ICON_PADDING = 8; +export const SPACE_BETWEEN_ITEMS = 16; +export const ICON_WIDTH = 16; +export const TEXT_MIN_WIDTH = 70; +export const TEXT_MAX_WIDTH = 150; +export const ORDER = { + dashboards: 0, + table: 1, + sql: 2, + rows: 3, + tags: 4, + description: 5, + owner: 6, + lastModified: 7, +}; diff --git a/superset-frontend/src/components/MetadataBar/index.tsx b/superset-frontend/src/components/MetadataBar/index.tsx index a40e4113b..dd72af2b0 100644 --- a/superset-frontend/src/components/MetadataBar/index.tsx +++ b/superset-frontend/src/components/MetadataBar/index.tsx @@ -16,7 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import MetadataBar, { MIN_NUMBER_ITEMS, MAX_NUMBER_ITEMS } from './MetadataBar'; +import MetadataBar from './MetadataBar'; +import { MIN_NUMBER_ITEMS, MAX_NUMBER_ITEMS } from './constants'; export type { MetadataBarProps } from './MetadataBar'; diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx index 01d542470..e49b136c0 100644 --- a/superset-frontend/src/dashboard/components/Header/index.jsx +++ b/superset-frontend/src/dashboard/components/Header/index.jsx @@ -44,7 +44,6 @@ import ConnectedHeaderActionsDropdown from 'src/dashboard/components/Header/Head import PublishedStatus from 'src/dashboard/components/PublishedStatus'; import UndoRedoKeyListeners from 'src/dashboard/components/UndoRedoKeyListeners'; import PropertiesModal from 'src/dashboard/components/PropertiesModal'; -import getOwnerName from 'src/utils/getOwnerName'; import { UNDO_LIMIT, SAVE_TYPE_OVERWRITE, @@ -55,7 +54,6 @@ 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'; import { @@ -88,6 +86,7 @@ import { logEvent } from '../../../logger/actions'; import { dashboardInfoChanged } from '../../actions/dashboardInfo'; import isDashboardLoading from '../../util/isDashboardLoading'; import { useChartIds } from '../../util/charts/useChartIds'; +import { useDashboardMetadataBar } from './useDashboardMetadataBar'; const extensionsRegistry = getExtensionsRegistry(); @@ -472,32 +471,7 @@ const Header = () => { setShowingEmbedModal(false); }, []); - const getMetadataItems = useCallback( - () => [ - { - 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, - }, - ], - [ - dashboardInfo.changed_by, - dashboardInfo.changed_on_delta_humanized, - dashboardInfo.created_by, - dashboardInfo.created_on_delta_humanized, - dashboardInfo.owners, - ], - ); + const metadataBar = useDashboardMetadataBar(dashboardInfo); const userCanEdit = dashboardInfo.dash_edit_perm && !dashboardInfo.is_managed_externally; @@ -579,15 +553,13 @@ const Header = () => { visible={!editMode} /> ), - !editMode && !isEmbedded && ( - - ), + !editMode && !isEmbedded && metadataBar, ], [ boundActionCreators.savePublished, dashboardInfo.id, editMode, - getMetadataItems, + metadataBar, isEmbedded, isPublished, userCanEdit, diff --git a/superset-frontend/src/dashboard/components/Header/useDashboardMetadataBar.tsx b/superset-frontend/src/dashboard/components/Header/useDashboardMetadataBar.tsx new file mode 100644 index 000000000..3ded29a67 --- /dev/null +++ b/superset-frontend/src/dashboard/components/Header/useDashboardMetadataBar.tsx @@ -0,0 +1,54 @@ +/** + * 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 { useMemo } from 'react'; +import { t } from '@superset-ui/core'; +import { DashboardInfo } from 'src/dashboard/types'; +import MetadataBar, { MetadataType } from 'src/components/MetadataBar'; +import getOwnerName from 'src/utils/getOwnerName'; + +export const useDashboardMetadataBar = (dashboardInfo: DashboardInfo) => { + const items = useMemo( + () => [ + { + type: MetadataType.LastModified as const, + value: dashboardInfo.changed_on_delta_humanized, + modifiedBy: + getOwnerName(dashboardInfo.changed_by) || t('Not available'), + }, + { + type: MetadataType.Owner as const, + 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, + }, + ], + [ + dashboardInfo.changed_by, + dashboardInfo.changed_on_delta_humanized, + dashboardInfo.created_by, + dashboardInfo.created_on_delta_humanized, + dashboardInfo.owners, + ], + ); + + return ; +}; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/FilterBarSettings.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/FilterBarSettings.test.tsx index 0e52e4def..f1b328292 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/FilterBarSettings.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/FilterBarSettings.test.tsx @@ -52,6 +52,9 @@ const initialState: { dashboardInfo: DashboardInfo } = { conf: {}, }, crossFiltersEnabled: true, + created_on_delta_humanized: '', + changed_on_delta_humanized: '', + owners: [], }, }; diff --git a/superset-frontend/src/dashboard/types.ts b/superset-frontend/src/dashboard/types.ts index f1b1c47b5..a643463dd 100644 --- a/superset-frontend/src/dashboard/types.ts +++ b/superset-frontend/src/dashboard/types.ts @@ -33,6 +33,7 @@ import Database from 'src/types/Database'; import { UrlParamEntries } from 'src/utils/urlUtils'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; +import Owner from 'src/types/Owner'; import { ChartState } from '../explore/types'; export type { Dashboard } from 'src/types/Dashboard'; @@ -139,6 +140,11 @@ export type DashboardInfo = { }; crossFiltersEnabled: boolean; filterBarOrientation: FilterBarOrientation; + created_on_delta_humanized: string; + changed_on_delta_humanized: string; + changed_by?: Owner; + created_by?: Owner; + owners: Owner[]; }; export type ChartsState = { [key: string]: Chart }; diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx index c7df2af40..20a97980d 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx @@ -16,12 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import PropTypes from 'prop-types'; import { Tooltip } from 'src/components/Tooltip'; -import { css, logging, SupersetClient, t, tn } from '@superset-ui/core'; +import { css, logging, SupersetClient, t } from '@superset-ui/core'; import { chartPropShape } from 'src/dashboard/util/propShapes'; import AlteredSliceTag from 'src/components/AlteredSliceTag'; import Button from 'src/components/Button'; @@ -29,10 +29,10 @@ import Icons from 'src/components/Icons'; import PropertiesModal from 'src/explore/components/PropertiesModal'; import { sliceUpdated } from 'src/explore/actions/exploreActions'; import { PageHeaderWithActions } from 'src/components/PageHeaderWithActions'; -import MetadataBar, { MetadataType } from 'src/components/MetadataBar'; import { setSaveChartModalVisibility } from 'src/explore/actions/saveModalActions'; import { applyColors, resetColors } from 'src/utils/colorScheme'; import { useExploreAdditionalActionsMenu } from '../useExploreAdditionalActionsMenu'; +import { useExploreMetadataBar } from './useExploreMetadataBar'; const propTypes = { actions: PropTypes.object.isRequired, @@ -160,48 +160,7 @@ export const ExploreChartHeader = ({ metadata?.dashboards, ); - const metadataBar = useMemo(() => { - if (!metadata) { - return null; - } - const items = []; - items.push({ - type: MetadataType.Dashboards, - title: - metadata.dashboards.length > 0 - ? tn( - 'Added to 1 dashboard', - 'Added to %s dashboards', - metadata.dashboards.length, - metadata.dashboards.length, - ) - : t('Not added to any dashboard'), - description: - metadata.dashboards.length > 0 - ? t( - 'You can preview the list of dashboards in the chart settings dropdown.', - ) - : undefined, - }); - items.push({ - type: MetadataType.LastModified, - value: metadata.changed_on_humanized, - modifiedBy: metadata.changed_by || t('Not available'), - }); - items.push({ - type: MetadataType.Owner, - createdBy: metadata.created_by || t('Not available'), - owners: metadata.owners.length > 0 ? metadata.owners : t('None'), - createdOn: metadata.created_on_humanized, - }); - if (slice?.description) { - items.push({ - type: MetadataType.Description, - value: slice?.description, - }); - } - return ; - }, [metadata, slice?.description]); + const metadataBar = useExploreMetadataBar(metadata, slice); const oldSliceName = slice?.slice_name; return ( diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/useExploreMetadataBar.tsx b/superset-frontend/src/explore/components/ExploreChartHeader/useExploreMetadataBar.tsx new file mode 100644 index 000000000..943d91efc --- /dev/null +++ b/superset-frontend/src/explore/components/ExploreChartHeader/useExploreMetadataBar.tsx @@ -0,0 +1,71 @@ +/** + * 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 { useMemo } from 'react'; +import { t, tn } from '@superset-ui/core'; +import MetadataBar, { MetadataType } from 'src/components/MetadataBar'; +import { ExplorePageInitialData } from 'src/explore/types'; + +export const useExploreMetadataBar = ( + metadata: ExplorePageInitialData['metadata'], + slice: ExplorePageInitialData['slice'], +) => + useMemo(() => { + if (!metadata) { + return null; + } + const items = []; + if (metadata.dashboards) { + items.push({ + type: MetadataType.Dashboards as const, + title: + metadata.dashboards.length > 0 + ? tn( + 'Added to 1 dashboard', + 'Added to %s dashboards', + metadata.dashboards.length, + metadata.dashboards.length, + ) + : t('Not added to any dashboard'), + description: + metadata.dashboards.length > 0 + ? t( + 'You can preview the list of dashboards in the chart settings dropdown.', + ) + : undefined, + }); + } + items.push({ + type: MetadataType.LastModified as const, + value: metadata.changed_on_humanized, + modifiedBy: metadata.changed_by || t('Not available'), + }); + items.push({ + type: MetadataType.Owner as const, + createdBy: metadata.created_by || t('Not available'), + owners: metadata.owners.length > 0 ? metadata.owners : t('None'), + createdOn: metadata.created_on_humanized, + }); + if (slice?.description) { + items.push({ + type: MetadataType.Description as const, + value: slice?.description, + }); + } + return ; + }, [metadata, slice?.description]); diff --git a/superset-frontend/src/explore/types.ts b/superset-frontend/src/explore/types.ts index 8ff0219fe..301c9a686 100644 --- a/superset-frontend/src/explore/types.ts +++ b/superset-frontend/src/explore/types.ts @@ -82,6 +82,10 @@ export interface ExplorePageInitialData { owners: string[]; created_by?: string; changed_by?: string; + dashboards?: { + id: number; + dashboard_title: string; + }[]; }; saveAction?: SaveActionType | null; }