ShareExternally: Rename public dashboard section (#91928)

This commit is contained in:
Juan Cabanas
2024-08-14 17:03:19 -03:00
committed by GitHub
parent 34ab5fe1f3
commit 07500e11be
8 changed files with 94 additions and 105 deletions
@@ -1,3 +1,4 @@
import { config } from '@grafana/runtime';
import { t } from 'app/core/internationalization'; import { t } from 'app/core/internationalization';
// Maps the ID of the nav item to a translated phrase to later pass to <Trans /> // Maps the ID of the nav item to a translated phrase to later pass to <Trans />
@@ -43,7 +44,9 @@ export function getNavTitle(navId: string | undefined) {
case 'reports': case 'reports':
return t('nav.reporting.title', 'Reporting'); return t('nav.reporting.title', 'Reporting');
case 'dashboards/public': case 'dashboards/public':
return t('nav.public.title', 'Public dashboards'); return config.featureToggles.newDashboardSharingComponent
? t('nav.shared-dashboard.title', 'Shared dashboards')
: t('nav.public.title', 'Public dashboards');
case 'dashboards/recently-deleted': case 'dashboards/recently-deleted':
return t('nav.recently-deleted.title', 'Recently deleted'); return t('nav.recently-deleted.title', 'Recently deleted');
case 'dashboards/new': case 'dashboards/new':
@@ -210,6 +213,10 @@ export function getNavSubTitle(navId: string | undefined) {
'nav.snapshots.subtitle', 'nav.snapshots.subtitle',
'Interactive, publically available, point-in-time representations of dashboards' 'Interactive, publically available, point-in-time representations of dashboards'
); );
case 'dashboards/public':
return config.featureToggles.newDashboardSharingComponent
? t('nav.shared-dashboard.subtitle', "Manage your organization's externally shared dashboards")
: undefined;
case 'dashboards/library-panels': case 'dashboards/library-panels':
return t('nav.library-panels.subtitle', 'Reusable panels that can be added to multiple dashboards'); return t('nav.library-panels.subtitle', 'Reusable panels that can be added to multiple dashboards');
case 'dashboards/recently-deleted': case 'dashboards/recently-deleted':
@@ -264,7 +264,6 @@ export function ConfigPublicDashboard({ publicDashboard, unsupportedDatasources
onRevoke={() => { onRevoke={() => {
DashboardInteractions.revokePublicDashboardClicked(); DashboardInteractions.revokePublicDashboardClicked();
showModal(DeletePublicDashboardModal, { showModal(DeletePublicDashboardModal, {
dashboardTitle: dashboard.title,
onConfirm: () => onDeletePublicDashboardClick(hideModal), onConfirm: () => onDeletePublicDashboardClick(hideModal),
onDismiss: () => { onDismiss: () => {
showModal(ShareModal, { showModal(ShareModal, {
@@ -1,5 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { config } from '@grafana/runtime';
import { Button, ModalsController, ButtonProps } from '@grafana/ui/src'; import { Button, ModalsController, ButtonProps } from '@grafana/ui/src';
import { t } from 'app/core/internationalization'; import { t } from 'app/core/internationalization';
import { useDeletePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi'; import { useDeletePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi';
@@ -41,17 +42,15 @@ export const DeletePublicDashboardButton = ({
return ( return (
<ModalsController> <ModalsController>
{({ showModal, hideModal }) => { {({ showModal, hideModal }) => {
const translatedRevocationButtonText = t( const translatedRevocationButtonText = config.featureToggles.newDashboardSharingComponent
'public-dashboard-list.button.revoke-button-text', ? t('shared-dashboard-list.button.revoke-button-text', 'Revoke access')
'Revoke public URL' : t('public-dashboard-list.button.revoke-button-text', 'Revoke public URL');
);
return ( return (
<Button <Button
aria-label={translatedRevocationButtonText} aria-label={translatedRevocationButtonText}
title={translatedRevocationButtonText} title={translatedRevocationButtonText}
onClick={() => onClick={() =>
showModal(DeletePublicDashboardModal, { showModal(DeletePublicDashboardModal, {
dashboardTitle: publicDashboard.title,
onConfirm: () => onDeletePublicDashboardClick(publicDashboard, hideModal), onConfirm: () => onDeletePublicDashboardClick(publicDashboard, hideModal),
onDismiss: () => { onDismiss: () => {
onDismiss ? onDismiss() : hideModal(); onDismiss ? onDismiss() : hideModal();
@@ -1,41 +1,42 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data/src'; import { GrafanaTheme2 } from '@grafana/data/src';
import { config } from '@grafana/runtime';
import { ConfirmModal, useStyles2 } from '@grafana/ui/src'; import { ConfirmModal, useStyles2 } from '@grafana/ui/src';
import { t } from 'app/core/internationalization'; import { t } from 'app/core/internationalization';
const Body = ({ title }: { title?: string }) => { const Body = () => {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
return ( return (
<p className={styles.description}> <p className={styles.description}>
{title {config.featureToggles.newDashboardSharingComponent
? t( ? t(
'public-dashboard.delete-modal.revoke-nonorphaned-body-text', 'shared-dashboard.delete-modal.revoke-body-text',
'Are you sure you want to revoke this URL? The dashboard will no longer be public.' 'Are you sure you want to revoke this access? The dashboard can no longer be shared.'
) )
: t( : t(
'public-dashboard.delete-modal.revoke-orphaned-body-text', 'public-dashboard.delete-modal.revoke-body-text',
'Orphaned public dashboard will no longer be public.' 'Are you sure you want to revoke this URL? The dashboard will no longer be public.'
)} )}
</p> </p>
); );
}; };
export const DeletePublicDashboardModal = ({ export const DeletePublicDashboardModal = ({
dashboardTitle,
onConfirm, onConfirm,
onDismiss, onDismiss,
}: { }: {
dashboardTitle?: string;
onConfirm: () => void; onConfirm: () => void;
onDismiss: () => void; onDismiss: () => void;
}) => { }) => {
const translatedRevocationModalText = t('public-dashboard.delete-modal.revoke-title', 'Revoke public URL'); const translatedRevocationModalText = config.featureToggles.newDashboardSharingComponent
? t('shared-dashboard.delete-modal.revoke-title', 'Revoke access')
: t('public-dashboard.delete-modal.revoke-title', 'Revoke public URL');
return ( return (
<ConfirmModal <ConfirmModal
isOpen isOpen
body={<Body title={dashboardTitle} />} body={<Body />}
onConfirm={onConfirm} onConfirm={onConfirm}
onDismiss={onDismiss} onDismiss={onDismiss}
title={translatedRevocationModalText} title={translatedRevocationModalText}
@@ -32,25 +32,6 @@ const publicDashboardListResponse: PublicDashboardListResponse[] = [
}, },
]; ];
const orphanedDashboardListResponse: PublicDashboardListResponse[] = [
{
uid: 'SdZwuCZVz2',
accessToken: 'beeaf92f6ab3467f80b2be922c7741ab',
title: '',
dashboardUid: '',
isEnabled: false,
slug: '',
},
{
uid: 'EuiEbd3nz2',
accessToken: '8687b0498ccf4babb2f92810d8563b33',
title: '',
dashboardUid: '',
isEnabled: true,
slug: '',
},
];
const paginationResponse: Omit<PublicDashboardListWithPaginationResponse, 'publicDashboards'> = { const paginationResponse: Omit<PublicDashboardListWithPaginationResponse, 'publicDashboards'> = {
page: 1, page: 1,
perPage: 50, perPage: 50,
@@ -165,26 +146,6 @@ describe('Delete public dashboard', () => {
}); });
}); });
describe('Orphaned public dashboard', () => {
it('renders orphaned and non orphaned public dashboards items correctly', async () => {
const response: PublicDashboardListWithPaginationResponse = {
...paginationResponse,
publicDashboards: [...publicDashboardListResponse, ...orphanedDashboardListResponse],
};
server.use(
http.get('/api/dashboards/public-dashboards', () => {
return HttpResponse.json(response);
})
);
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
await renderPublicDashboardTable(true);
response.publicDashboards.forEach((pd, idx) => {
renderPublicDashboardItemCorrectly(pd, idx, true);
});
});
});
const renderPublicDashboardItemCorrectly = (pd: PublicDashboardListResponse, idx: number, hasWriteAccess: boolean) => { const renderPublicDashboardItemCorrectly = (pd: PublicDashboardListResponse, idx: number, hasWriteAccess: boolean) => {
const isOrphaned = !pd.dashboardUid; const isOrphaned = !pd.dashboardUid;
@@ -4,23 +4,21 @@ import { useMedia } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { reportInteraction } from '@grafana/runtime'; import { config, reportInteraction } from '@grafana/runtime';
import { import {
LinkButton,
useStyles2,
Spinner,
Card, Card,
useTheme2,
Tooltip,
Icon,
Switch,
Pagination,
HorizontalGroup,
EmptyState, EmptyState,
HorizontalGroup,
LinkButton,
Pagination,
Spinner,
Switch,
TextLink, TextLink,
useStyles2,
useTheme2,
} from '@grafana/ui'; } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page'; import { Page } from 'app/core/components/Page/Page';
import { Trans, t } from 'app/core/internationalization'; import { t, Trans } from 'app/core/internationalization';
import { contextSrv } from 'app/core/services/context_srv'; import { contextSrv } from 'app/core/services/context_srv';
import { import {
useListPublicDashboardsQuery, useListPublicDashboardsQuery,
@@ -45,7 +43,6 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
const selectors = e2eSelectors.pages.PublicDashboards; const selectors = e2eSelectors.pages.PublicDashboards;
const hasWritePermissions = contextSrv.hasPermission(AccessControlAction.DashboardsPublicWrite); const hasWritePermissions = contextSrv.hasPermission(AccessControlAction.DashboardsPublicWrite);
const isOrphaned = !pd.dashboardUid;
const onTogglePause = (pd: PublicDashboardListResponse, isPaused: boolean) => { const onTogglePause = (pd: PublicDashboardListResponse, isPaused: boolean) => {
const req = { const req = {
@@ -60,29 +57,16 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
}; };
const CardActions = useMemo(() => (isMobile ? Card.Actions : Card.SecondaryActions), [isMobile]); const CardActions = useMemo(() => (isMobile ? Card.Actions : Card.SecondaryActions), [isMobile]);
const translatedPauseSharingText = t('public-dashboard-list.toggle.pause-sharing-toggle-text', 'Pause sharing');
const isNewSharingComponentEnabled = config.featureToggles.newDashboardSharingComponent;
const translatedPauseSharingText = isNewSharingComponentEnabled
? t('shared-dashboard-list.toggle.pause-sharing-toggle-text', 'Pause access')
: t('public-dashboard-list.toggle.pause-sharing-toggle-text', 'Pause sharing');
return ( return (
<Card className={styles.card} href={!isOrphaned ? `/d/${pd.dashboardUid}` : undefined}> <Card className={styles.card} href={`/d/${pd.dashboardUid}`}>
<Card.Heading className={styles.heading}> <Card.Heading className={styles.heading}>
{!isOrphaned ? ( <span>{pd.title}</span>
<span>{pd.title}</span>
) : (
<Tooltip
content={t(
'public-dashboard-list.dashboard-title.orphaned-tooltip',
'The linked dashboard has already been deleted'
)}
placement="top"
>
<div className={styles.orphanedTitle}>
<Trans i18nKey="public-dashboard-list.dashboard-title.orphaned-title">
<span>Orphaned public dashboard</span>
</Trans>
<Icon name="info-circle" />
</div>
</Tooltip>
)}
</Card.Heading> </Card.Heading>
<CardActions className={styles.actions}> <CardActions className={styles.actions}>
<div className={styles.pauseSwitch}> <div className={styles.pauseSwitch}>
@@ -101,7 +85,6 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
<span>{translatedPauseSharingText}</span> <span>{translatedPauseSharingText}</span>
</div> </div>
<LinkButton <LinkButton
disabled={isOrphaned}
fill="text" fill="text"
icon="external-link-alt" icon="external-link-alt"
variant="secondary" variant="secondary"
@@ -109,18 +92,25 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
color={theme.colors.warning.text} color={theme.colors.warning.text}
href={generatePublicDashboardUrl(pd.accessToken)} href={generatePublicDashboardUrl(pd.accessToken)}
key="public-dashboard-url" key="public-dashboard-url"
tooltip={t('public-dashboard-list.button.view-button-tooltip', 'View public dashboard')} tooltip={
isNewSharingComponentEnabled
? t('shared-dashboard-list.button.view-button-tooltip', 'View shared dashboard')
: t('public-dashboard-list.button.view-button-tooltip', 'View public dashboard')
}
data-testid={selectors.ListItem.linkButton} data-testid={selectors.ListItem.linkButton}
/> />
<LinkButton <LinkButton
disabled={isOrphaned}
fill="text" fill="text"
icon="cog" icon="cog"
variant="secondary" variant="secondary"
color={theme.colors.warning.text} color={theme.colors.warning.text}
href={generatePublicDashboardConfigUrl(pd.dashboardUid, pd.slug)} href={generatePublicDashboardConfigUrl(pd.dashboardUid, pd.slug)}
key="public-dashboard-config-url" key="public-dashboard-config-url"
tooltip={t('public-dashboard-list.button.config-button-tooltip', 'Configure public dashboard')} tooltip={
isNewSharingComponentEnabled
? t('shared-dashboard-list.button.config-button-tooltip', 'Configure shared dashboard')
: t('public-dashboard-list.button.config-button-tooltip', 'Configure public dashboard')
}
data-testid={selectors.ListItem.configButton} data-testid={selectors.ListItem.configButton}
/> />
{hasWritePermissions && ( {hasWritePermissions && (
@@ -129,7 +119,11 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
icon="trash-alt" icon="trash-alt"
variant="secondary" variant="secondary"
publicDashboard={pd} publicDashboard={pd}
tooltip={t('public-dashboard-list.button.revoke-button-tooltip', 'Revoke public dashboard URL')} tooltip={
isNewSharingComponentEnabled
? t('shared-dashboard-list.button.revoke-button-tooltip', 'Revoke access')
: t('public-dashboard-list.button.revoke-button-tooltip', 'Revoke public dashboard URL')
}
loader={<Spinner />} loader={<Spinner />}
data-testid={selectors.ListItem.trashcanButton} data-testid={selectors.ListItem.trashcanButton}
/> />
+20 -6
View File
@@ -1519,6 +1519,10 @@
"subtitle": "Use service accounts to run automated workloads in Grafana", "subtitle": "Use service accounts to run automated workloads in Grafana",
"title": "Service accounts" "title": "Service accounts"
}, },
"shared-dashboard": {
"subtitle": "Manage your organization's externally shared dashboards",
"title": "Shared dashboards"
},
"sign-out": { "sign-out": {
"title": "Sign out" "title": "Sign out"
}, },
@@ -1744,8 +1748,7 @@
"welcome-title": "Welcome to public dashboards!" "welcome-title": "Welcome to public dashboards!"
}, },
"delete-modal": { "delete-modal": {
"revoke-nonorphaned-body-text": "Are you sure you want to revoke this URL? The dashboard will no longer be public.", "revoke-body-text": "Are you sure you want to revoke this URL? The dashboard will no longer be public.",
"revoke-orphaned-body-text": "Orphaned public dashboard will no longer be public.",
"revoke-title": "Revoke public URL" "revoke-title": "Revoke public URL"
}, },
"email-sharing": { "email-sharing": {
@@ -1844,10 +1847,6 @@
"revoke-button-tooltip": "Revoke public dashboard URL", "revoke-button-tooltip": "Revoke public dashboard URL",
"view-button-tooltip": "View public dashboard" "view-button-tooltip": "View public dashboard"
}, },
"dashboard-title": {
"orphaned-title": "<0>Orphaned public dashboard</0>",
"orphaned-tooltip": "The linked dashboard has already been deleted"
},
"empty-state": { "empty-state": {
"message": "You haven't created any public dashboards yet", "message": "You haven't created any public dashboards yet",
"more-info": "Create a public dashboard from any existing dashboard through the <1>Share</1> modal. <4>Learn more</4>" "more-info": "Create a public dashboard from any existing dashboard through the <1>Share</1> modal. <4>Learn more</4>"
@@ -2160,10 +2159,25 @@
} }
}, },
"shared-dashboard": { "shared-dashboard": {
"delete-modal": {
"revoke-body-text": "Are you sure you want to revoke this access? The dashboard can no longer be shared.",
"revoke-title": "Revoke access"
},
"fields": { "fields": {
"timezone-label": "Timezone" "timezone-label": "Timezone"
} }
}, },
"shared-dashboard-list": {
"button": {
"config-button-tooltip": "Configure shared dashboard",
"revoke-button-text": "Revoke access",
"revoke-button-tooltip": "Revoke access",
"view-button-tooltip": "View shared dashboard"
},
"toggle": {
"pause-sharing-toggle-text": "Pause access"
}
},
"shared-preferences": { "shared-preferences": {
"fields": { "fields": {
"home-dashboard-label": "Home Dashboard", "home-dashboard-label": "Home Dashboard",
+20 -6
View File
@@ -1519,6 +1519,10 @@
"subtitle": "Ůşę şęřvįčę äččőūʼnŧş ŧő řūʼn äūŧőmäŧęđ ŵőřĸľőäđş įʼn Ğřäƒäʼnä", "subtitle": "Ůşę şęřvįčę äččőūʼnŧş ŧő řūʼn äūŧőmäŧęđ ŵőřĸľőäđş įʼn Ğřäƒäʼnä",
"title": "Ŝęřvįčę äččőūʼnŧş" "title": "Ŝęřvįčę äččőūʼnŧş"
}, },
"shared-dashboard": {
"subtitle": "Mäʼnäģę yőūř őřģäʼnįžäŧįőʼn'ş ęχŧęřʼnäľľy şĥäřęđ đäşĥþőäřđş",
"title": "Ŝĥäřęđ đäşĥþőäřđş"
},
"sign-out": { "sign-out": {
"title": "Ŝįģʼn őūŧ" "title": "Ŝįģʼn őūŧ"
}, },
@@ -1744,8 +1748,7 @@
"welcome-title": "Ŵęľčőmę ŧő pūþľįč đäşĥþőäřđş!" "welcome-title": "Ŵęľčőmę ŧő pūþľįč đäşĥþőäřđş!"
}, },
"delete-modal": { "delete-modal": {
"revoke-nonorphaned-body-text": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő řęvőĸę ŧĥįş ŮŖĿ? Ŧĥę đäşĥþőäřđ ŵįľľ ʼnő ľőʼnģęř þę pūþľįč.", "revoke-body-text": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő řęvőĸę ŧĥįş ŮŖĿ? Ŧĥę đäşĥþőäřđ ŵįľľ ʼnő ľőʼnģęř þę pūþľįč.",
"revoke-orphaned-body-text": "Øřpĥäʼnęđ pūþľįč đäşĥþőäřđ ŵįľľ ʼnő ľőʼnģęř þę pūþľįč.",
"revoke-title": "Ŗęvőĸę pūþľįč ŮŖĿ" "revoke-title": "Ŗęvőĸę pūþľįč ŮŖĿ"
}, },
"email-sharing": { "email-sharing": {
@@ -1844,10 +1847,6 @@
"revoke-button-tooltip": "Ŗęvőĸę pūþľįč đäşĥþőäřđ ŮŖĿ", "revoke-button-tooltip": "Ŗęvőĸę pūþľįč đäşĥþőäřđ ŮŖĿ",
"view-button-tooltip": "Vįęŵ pūþľįč đäşĥþőäřđ" "view-button-tooltip": "Vįęŵ pūþľįč đäşĥþőäřđ"
}, },
"dashboard-title": {
"orphaned-title": "<0>Øřpĥäʼnęđ pūþľįč đäşĥþőäřđ</0>",
"orphaned-tooltip": "Ŧĥę ľįʼnĸęđ đäşĥþőäřđ ĥäş äľřęäđy þęęʼn đęľęŧęđ"
},
"empty-state": { "empty-state": {
"message": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny pūþľįč đäşĥþőäřđş yęŧ", "message": "Ÿőū ĥävęʼn'ŧ čřęäŧęđ äʼny pūþľįč đäşĥþőäřđş yęŧ",
"more-info": "Cřęäŧę ä pūþľįč đäşĥþőäřđ ƒřőm äʼny ęχįşŧįʼnģ đäşĥþőäřđ ŧĥřőūģĥ ŧĥę <1>Ŝĥäřę</1> mőđäľ. <4>Ŀęäřʼn mőřę</4>" "more-info": "Cřęäŧę ä pūþľįč đäşĥþőäřđ ƒřőm äʼny ęχįşŧįʼnģ đäşĥþőäřđ ŧĥřőūģĥ ŧĥę <1>Ŝĥäřę</1> mőđäľ. <4>Ŀęäřʼn mőřę</4>"
@@ -2160,10 +2159,25 @@
} }
}, },
"shared-dashboard": { "shared-dashboard": {
"delete-modal": {
"revoke-body-text": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő řęvőĸę ŧĥįş äččęşş? Ŧĥę đäşĥþőäřđ čäʼn ʼnő ľőʼnģęř þę şĥäřęđ.",
"revoke-title": "Ŗęvőĸę äččęşş"
},
"fields": { "fields": {
"timezone-label": "Ŧįmęžőʼnę" "timezone-label": "Ŧįmęžőʼnę"
} }
}, },
"shared-dashboard-list": {
"button": {
"config-button-tooltip": "Cőʼnƒįģūřę şĥäřęđ đäşĥþőäřđ",
"revoke-button-text": "Ŗęvőĸę äččęşş",
"revoke-button-tooltip": "Ŗęvőĸę äččęşş",
"view-button-tooltip": "Vįęŵ şĥäřęđ đäşĥþőäřđ"
},
"toggle": {
"pause-sharing-toggle-text": "Päūşę äččęşş"
}
},
"shared-preferences": { "shared-preferences": {
"fields": { "fields": {
"home-dashboard-label": "Ħőmę Đäşĥþőäřđ", "home-dashboard-label": "Ħőmę Đäşĥþőäřđ",