Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,13 @@ describe('AllEntitiesTable', () => {
jest.restoreAllMocks();
});

it('renders when empty', () => {
it('renders when empty with button to tag if user has perm', () => {
render(
<AllEntitiesTable
search=""
setShowTagModal={mockSetShowTagModal}
objects={mockObjects}
canEditTag
/>,
{ useRouter: true },
);
Expand All @@ -108,25 +109,70 @@ describe('AllEntitiesTable', () => {
expect(screen.getByText('Add tag to entities')).toBeInTheDocument();
});

it('renders when empty without button to tag if user does not have perm', () => {
render(
<AllEntitiesTable
search=""
setShowTagModal={mockSetShowTagModal}
objects={mockObjects}
canEditTag={false}
/>,
{ useRouter: true },
);

expect(
screen.getByText('No entities have this tag currently assigned'),
).toBeInTheDocument();

expect(screen.queryByText('Add tag to entities')).not.toBeInTheDocument();
});

it('renders the correct tags for each object type, excluding the current tag', () => {
render(
<AllEntitiesTable
search=""
setShowTagModal={mockSetShowTagModal}
objects={mockObjectsWithTags}
canEditTag
/>,
{ useRouter: true },
);

expect(screen.getByText('Dashboards')).toBeInTheDocument();
expect(screen.getByText('Sales Dashboard')).toBeInTheDocument();
expect(screen.getByText('Sales')).toBeInTheDocument();

expect(screen.getByText('Charts')).toBeInTheDocument();
expect(screen.getByText('Monthly Revenue')).toBeInTheDocument();
expect(screen.getByText('Revenue')).toBeInTheDocument();

expect(screen.getByText('Queries')).toBeInTheDocument();
expect(screen.getByText('User Engagement')).toBeInTheDocument();
expect(screen.getByText('Engagement')).toBeInTheDocument();

expect(screen.queryByText('Current Tag')).not.toBeInTheDocument();
});

it('Only list asset types that have entities', () => {
const mockObjects = {
dashboard: [],
chart: [mockObjectsWithTags.chart[0]],
query: [],
};

render(
<AllEntitiesTable
search=""
setShowTagModal={mockSetShowTagModal}
objects={mockObjects}
canEditTag
/>,
{ useRouter: true },
);

expect(screen.queryByText('Dashboards')).not.toBeInTheDocument();
expect(screen.getByText('Charts')).toBeInTheDocument();
expect(screen.getByText('Monthly Revenue')).toBeInTheDocument();
expect(screen.queryByText('Queries')).not.toBeInTheDocument();
});
});
52 changes: 35 additions & 17 deletions superset-frontend/src/features/allEntities/AllEntitiesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,22 @@ interface AllEntitiesTableProps {
search?: string;
setShowTagModal: (show: boolean) => void;
objects: TaggedObjects;
canEditTag: boolean;
}

export default function AllEntitiesTable({
search = '',
setShowTagModal,
objects,
canEditTag,
}: AllEntitiesTableProps) {
type objectType = 'dashboard' | 'chart' | 'query';

const [tagId] = useQueryParam('id', NumberParam);
const showListViewObjs =
objects.dashboard.length > 0 ||
objects.chart.length > 0 ||
objects.query.length > 0;
const showDashboardList = objects.dashboard.length > 0;
const showChartList = objects.chart.length > 0;
const showQueryList = objects.query.length > 0;
const showListViewObjs = showDashboardList || showChartList || showQueryList;

const renderTable = (type: objectType) => {
const data = objects[type].map((o: TaggedObject) => ({
Expand Down Expand Up @@ -134,21 +136,37 @@ export default function AllEntitiesTable({
<AllEntitiesTableContainer>
{showListViewObjs ? (
<>
<div className="entity-title">{t('Dashboards')}</div>
{renderTable('dashboard')}
<div className="entity-title">{t('Charts')}</div>
{renderTable('chart')}
<div className="entity-title">{t('Queries')}</div>
{renderTable('query')}
{showDashboardList && (
<>
<div className="entity-title">{t('Dashboards')}</div>
{renderTable('dashboard')}
</>
)}
{showChartList && (
<>
<div className="entity-title">{t('Charts')}</div>
{renderTable('chart')}
</>
)}
{showQueryList && (
<>
<div className="entity-title">{t('Queries')}</div>
{renderTable('query')}
</>
)}
</>
) : (
<EmptyState
image="dashboard.svg"
size="large"
title={t('No entities have this tag currently assigned')}
buttonAction={() => setShowTagModal(true)}
buttonText={t('Add tag to entities')}
/>
<>
Comment thread
Vitor-Avila marked this conversation as resolved.
Outdated
<EmptyState
image="dashboard.svg"
size="large"
title={t('No entities have this tag currently assigned')}
{...(canEditTag && {
buttonAction: () => setShowTagModal(true),
buttonText: t('Add tag to entities'),
})}
/>
</>
)}
</AllEntitiesTableContainer>
);
Expand Down
26 changes: 18 additions & 8 deletions superset-frontend/src/pages/AllEntities/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import { fetchObjectsByTagIds, fetchSingleTag } from 'src/features/tags/tags';
import Loading from 'src/components/Loading';
import getOwnerName from 'src/utils/getOwnerName';
import { TaggedObject, TaggedObjects } from 'src/types/TaggedObject';
import { findPermission } from 'src/utils/findPermission';
import { useSelector } from 'react-redux';
import { RootState } from 'src/dashboard/types';

const additionalItemsStyles = (theme: SupersetTheme) => css`
display: flex;
Expand Down Expand Up @@ -100,6 +103,10 @@ function AllEntities() {
query: [],
});

const canEditTag = useSelector((state: RootState) =>
findPermission('can_write', 'Tag', state.user?.roles),
);
Comment on lines +106 to +108

This comment was marked as resolved.


const editableTitleProps = {
title: tag?.name || '',
placeholder: 'testing',
Expand Down Expand Up @@ -211,14 +218,16 @@ function AllEntities() {
}
rightPanelAdditionalItems={
<>
<Button
data-test="bulk-select-action"
buttonStyle="secondary"
onClick={() => setShowTagModal(true)}
showMarginRight={false}
>
{t('Edit Tag')}{' '}
</Button>
{canEditTag && (
<Button
data-test="bulk-select-action"
buttonStyle="secondary"
onClick={() => setShowTagModal(true)}
showMarginRight={false}
>
{t('Edit tag')}{' '}
</Button>
)}
</>
}
menuDropdownProps={{
Expand All @@ -232,6 +241,7 @@ function AllEntities() {
search={tag?.name || ''}
setShowTagModal={setShowTagModal}
objects={objects}
canEditTag={canEditTag}
/>
</div>
</AllEntitiesContainer>
Expand Down
3 changes: 2 additions & 1 deletion superset/views/all_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from flask_appbuilder.security.decorators import has_access

from superset import is_feature_enabled
from superset.constants import RouteMethod
from superset.superset_typing import FlaskResponse
from superset.tags.models import Tag
from superset.views.base import SupersetModelView
Expand All @@ -33,7 +34,7 @@ class TaggedObjectsModelView(SupersetModelView):
route_base = "/superset/all_entities"
datamodel = SQLAInterface(Tag)
class_permission_name = "Tags"
include_route_methods = {"list"}
include_route_methods = {RouteMethod.LIST}

@has_access
@expose("/")
Expand Down