feat: Adds the MetadataBar to the Explore header (#21560)

This commit is contained in:
Michael S. Molina
2022-09-29 14:34:57 -03:00
committed by GitHub
parent 5ea9249059
commit 0dda5fe1cf
19 changed files with 258 additions and 104 deletions

View File

@@ -36,7 +36,7 @@ window.featureFlags = {
[FeatureFlag.EMBEDDABLE_CHARTS]: true,
};
const createProps = () => ({
const createProps = (additionalProps = {}) => ({
chart: {
id: 1,
latestQueryFormData: {
@@ -63,7 +63,7 @@ const createProps = () => ({
changed_on: '2021-03-19T16:30:56.750230',
changed_on_humanized: '7 days ago',
datasource: 'FCC 2018 Survey',
description: null,
description: 'Simple description',
description_markeddown: '',
edit_url: '/chart/edit/318',
form_data: {
@@ -106,10 +106,19 @@ const createProps = () => ({
user: {
userId: 1,
},
metadata: {
created_on_humanized: 'a week ago',
changed_on_humanized: '2 days ago',
owners: ['John Doe'],
created_by: 'John Doe',
changed_by: 'John Doe',
dashboards: [{ id: 1, dashboard_title: 'Test' }],
},
onSaveChart: jest.fn(),
canOverwrite: false,
canDownload: false,
isStarred: false,
...additionalProps,
});
fetchMock.post(
@@ -147,6 +156,27 @@ test('Cancelling changes to the properties should reset previous properties', as
expect(await screen.findByDisplayValue(prevChartName)).toBeInTheDocument();
});
test('renders the metadata bar when saved', async () => {
const props = createProps({ showTitlePanelItems: true });
render(<ExploreHeader {...props} />, { useRedux: true });
expect(
await screen.findByText('Added to 1 dashboard(s)'),
).toBeInTheDocument();
expect(await screen.findByText('Simple description')).toBeInTheDocument();
expect(await screen.findByText('John Doe')).toBeInTheDocument();
expect(await screen.findByText('2 days ago')).toBeInTheDocument();
});
test('does not render the metadata bar when not saved', async () => {
const props = createProps({ showTitlePanelItems: true, slice: null });
render(<ExploreHeader {...props} />, { useRedux: true });
await waitFor(() =>
expect(
screen.queryByText('Added to 1 dashboard(s)'),
).not.toBeInTheDocument(),
);
});
test('Save chart', async () => {
const props = createProps();
render(<ExploreHeader {...props} />, { useRedux: true });

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import PropTypes from 'prop-types';
@@ -24,6 +24,7 @@ import { Tooltip } from 'src/components/Tooltip';
import {
CategoricalColorNamespace,
css,
logging,
SupersetClient,
t,
} from '@superset-ui/core';
@@ -35,6 +36,7 @@ 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 { useExploreAdditionalActionsMenu } from '../useExploreAdditionalActionsMenu';
const propTypes = {
@@ -60,6 +62,15 @@ const saveButtonStyles = theme => css`
}
`;
const additionalItemsStyles = theme => css`
display: flex;
align-items: center;
margin-left: ${theme.gridUnit}px;
& > span {
margin-right: ${theme.gridUnit * 3}px;
}
`;
export const ExploreChartHeader = ({
dashboardId,
slice,
@@ -75,51 +86,51 @@ export const ExploreChartHeader = ({
sliceName,
onSaveChart,
saveDisabled,
metadata,
}) => {
const { latestQueryFormData, sliceFormData } = chart;
const [isPropertiesModalOpen, setIsPropertiesModalOpen] = useState(false);
const fetchChartDashboardData = async () => {
await SupersetClient.get({
endpoint: `/api/v1/chart/${slice.slice_id}`,
})
.then(res => {
const response = res?.json?.result;
if (response && response.dashboards && response.dashboards.length) {
const { dashboards } = response;
const dashboard =
dashboardId &&
dashboards.length &&
dashboards.find(d => d.id === dashboardId);
const updateCategoricalNamespace = async () => {
const { dashboards } = metadata || {};
const dashboard =
dashboardId && dashboards && dashboards.find(d => d.id === dashboardId);
if (dashboard && dashboard.json_metadata) {
// setting the chart to use the dashboard custom label colors if any
const metadata = JSON.parse(dashboard.json_metadata);
const sharedLabelColors = metadata.shared_label_colors || {};
const customLabelColors = metadata.label_colors || {};
const mergedLabelColors = {
...sharedLabelColors,
...customLabelColors,
};
if (dashboard) {
try {
// Dashboards from metadata don't contain the json_metadata field
// to avoid unnecessary payload. Here we query for the dashboard json_metadata.
const response = await SupersetClient.get({
endpoint: `/api/v1/dashboard/${dashboard.id}`,
});
const result = response?.json?.result;
const categoricalNamespace =
CategoricalColorNamespace.getNamespace();
// setting the chart to use the dashboard custom label colors if any
const metadata = JSON.parse(result.json_metadata);
const sharedLabelColors = metadata.shared_label_colors || {};
const customLabelColors = metadata.label_colors || {};
const mergedLabelColors = {
...sharedLabelColors,
...customLabelColors,
};
Object.keys(mergedLabelColors).forEach(label => {
categoricalNamespace.setColor(
label,
mergedLabelColors[label],
metadata.color_scheme,
);
});
}
}
})
.catch(() => {});
const categoricalNamespace = CategoricalColorNamespace.getNamespace();
Object.keys(mergedLabelColors).forEach(label => {
categoricalNamespace.setColor(
label,
mergedLabelColors[label],
metadata.color_scheme,
);
});
} catch (error) {
logging.info(t('Unable to retrieve dashboard colors'));
}
}
};
useEffect(() => {
if (dashboardId) fetchChartDashboardData();
if (dashboardId) updateCategoricalNamespace();
}, []);
const openPropertiesModal = () => {
@@ -140,6 +151,38 @@ export const ExploreChartHeader = ({
ownState,
);
const metadataBar = useMemo(() => {
if (!metadata) {
return null;
}
const items = [];
items.push({
type: MetadataType.DASHBOARDS,
title:
metadata.dashboards.length > 0
? t('Added to %s dashboard(s)', metadata.dashboards.length)
: t('Not added to any dashboard'),
});
items.push({
type: MetadataType.LAST_MODIFIED,
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 <MetadataBar items={items} />;
}, [metadata, slice?.description]);
const oldSliceName = slice?.slice_name;
return (
<>
@@ -168,16 +211,19 @@ export const ExploreChartHeader = ({
showTooltip: true,
}}
titlePanelAdditionalItems={
sliceFormData ? (
<AlteredSliceTag
className="altered"
origFormData={{
...sliceFormData,
chartTitle: oldSliceName,
}}
currentFormData={{ ...formData, chartTitle: sliceName }}
/>
) : null
<div css={additionalItemsStyles}>
{sliceFormData ? (
<AlteredSliceTag
className="altered"
origFormData={{
...sliceFormData,
chartTitle: oldSliceName,
}}
currentFormData={{ ...formData, chartTitle: sliceName }}
/>
) : null}
{metadataBar}
</div>
}
rightPanelAdditionalItems={
<Tooltip