mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Data sources: Add skeleton loader (#79016)
* refactor out DataSourcesListCard * add skeleton * increase gap between buttons on skeleton to match real component * lineHeight: 1 instead of 0 * refactor out ternary
This commit is contained in:
parent
5aff3389f4
commit
1efd21c08a
@ -4,16 +4,15 @@ import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { DataSourceSettings, GrafanaTheme2 } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { LinkButton, Card, Tag, useStyles2 } from '@grafana/ui';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { StoreState, AccessControlAction, useSelector } from 'app/types';
|
||||
|
||||
import { getDataSources, getDataSourcesCount, useDataSourcesRoutes, useLoadDataSources } from '../state';
|
||||
import { trackCreateDashboardClicked, trackExploreClicked, trackDataSourcesListViewed } from '../tracking';
|
||||
import { constructDataSourceExploreUrl } from '../utils';
|
||||
import { trackDataSourcesListViewed } from '../tracking';
|
||||
|
||||
import { DataSourcesListCard } from './DataSourcesListCard';
|
||||
import { DataSourcesListHeader } from './DataSourcesListHeader';
|
||||
|
||||
export function DataSourcesList() {
|
||||
@ -65,11 +64,7 @@ export function DataSourcesListView({
|
||||
});
|
||||
}, [location]);
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader />;
|
||||
}
|
||||
|
||||
if (dataSourcesCount === 0) {
|
||||
if (!isLoading && dataSourcesCount === 0) {
|
||||
return (
|
||||
<EmptyListCTA
|
||||
buttonDisabled={!hasCreateRights}
|
||||
@ -85,74 +80,31 @@ export function DataSourcesListView({
|
||||
);
|
||||
}
|
||||
|
||||
const getDataSourcesList = () => {
|
||||
if (isLoading) {
|
||||
return new Array(20)
|
||||
.fill(null)
|
||||
.map((_, index) => <DataSourcesListCard.Skeleton key={index} hasExploreRights={hasExploreRights} />);
|
||||
}
|
||||
|
||||
return dataSources.map((dataSource) => (
|
||||
<li key={dataSource.uid}>
|
||||
<DataSourcesListCard
|
||||
dataSource={dataSource}
|
||||
hasWriteRights={hasWriteRights}
|
||||
hasExploreRights={hasExploreRights}
|
||||
/>
|
||||
</li>
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* List Header */}
|
||||
<DataSourcesListHeader />
|
||||
|
||||
{/* List */}
|
||||
<ul className={styles.list}>
|
||||
{dataSources.map((dataSource) => {
|
||||
const dsLink = config.appSubUrl + dataSourcesRoutes.Edit.replace(/:uid/gi, dataSource.uid);
|
||||
return (
|
||||
<li key={dataSource.uid}>
|
||||
<Card href={hasWriteRights ? dsLink : undefined}>
|
||||
<Card.Heading>{dataSource.name}</Card.Heading>
|
||||
<Card.Figure>
|
||||
<img src={dataSource.typeLogoUrl} alt="" height="40px" width="40px" className={styles.logo} />
|
||||
</Card.Figure>
|
||||
<Card.Meta>
|
||||
{[
|
||||
dataSource.typeName,
|
||||
dataSource.url,
|
||||
dataSource.isDefault && <Tag key="default-tag" name={'default'} colorIndex={1} />,
|
||||
]}
|
||||
</Card.Meta>
|
||||
<Card.Tags>
|
||||
{/* Build Dashboard */}
|
||||
<LinkButton
|
||||
icon="apps"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
href={`dashboard/new-with-ds/${dataSource.uid}`}
|
||||
onClick={() => {
|
||||
trackCreateDashboardClicked({
|
||||
grafana_version: config.buildInfo.version,
|
||||
datasource_uid: dataSource.uid,
|
||||
plugin_name: dataSource.typeName,
|
||||
path: location.pathname,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Build a dashboard
|
||||
</LinkButton>
|
||||
|
||||
{/* Explore */}
|
||||
{hasExploreRights && (
|
||||
<LinkButton
|
||||
icon="compass"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
className={styles.button}
|
||||
href={constructDataSourceExploreUrl(dataSource)}
|
||||
onClick={() => {
|
||||
trackExploreClicked({
|
||||
grafana_version: config.buildInfo.version,
|
||||
datasource_uid: dataSource.uid,
|
||||
plugin_name: dataSource.typeName,
|
||||
path: location.pathname,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Explore
|
||||
</LinkButton>
|
||||
)}
|
||||
</Card.Tags>
|
||||
</Card>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<ul className={styles.list}>{getDataSourcesList()}</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -164,11 +116,5 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
display: 'grid',
|
||||
// gap: '8px', Add back when legacy support for old Card interface is dropped
|
||||
}),
|
||||
logo: css({
|
||||
objectFit: 'contain',
|
||||
}),
|
||||
button: css({
|
||||
marginLeft: theme.spacing(2),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,128 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
import { DataSourceSettings, GrafanaTheme2 } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Card, LinkButton, Stack, Tag, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { useDataSourcesRoutes } from '../state';
|
||||
import { trackCreateDashboardClicked, trackExploreClicked } from '../tracking';
|
||||
import { constructDataSourceExploreUrl } from '../utils';
|
||||
|
||||
export interface Props {
|
||||
dataSource: DataSourceSettings;
|
||||
hasWriteRights: boolean;
|
||||
hasExploreRights: boolean;
|
||||
}
|
||||
|
||||
export function DataSourcesListCard({ dataSource, hasWriteRights, hasExploreRights }: Props) {
|
||||
const dataSourcesRoutes = useDataSourcesRoutes();
|
||||
const dsLink = config.appSubUrl + dataSourcesRoutes.Edit.replace(/:uid/gi, dataSource.uid);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<Card href={hasWriteRights ? dsLink : undefined}>
|
||||
<Card.Heading>{dataSource.name}</Card.Heading>
|
||||
<Card.Figure>
|
||||
<img src={dataSource.typeLogoUrl} alt="" height="40px" width="40px" className={styles.logo} />
|
||||
</Card.Figure>
|
||||
<Card.Meta>
|
||||
{[
|
||||
dataSource.typeName,
|
||||
dataSource.url,
|
||||
dataSource.isDefault && <Tag key="default-tag" name={'default'} colorIndex={1} />,
|
||||
]}
|
||||
</Card.Meta>
|
||||
<Card.Tags>
|
||||
{/* Build Dashboard */}
|
||||
<LinkButton
|
||||
icon="apps"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
href={`dashboard/new-with-ds/${dataSource.uid}`}
|
||||
onClick={() => {
|
||||
trackCreateDashboardClicked({
|
||||
grafana_version: config.buildInfo.version,
|
||||
datasource_uid: dataSource.uid,
|
||||
plugin_name: dataSource.typeName,
|
||||
path: location.pathname,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Build a dashboard
|
||||
</LinkButton>
|
||||
|
||||
{/* Explore */}
|
||||
{hasExploreRights && (
|
||||
<LinkButton
|
||||
icon="compass"
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
className={styles.button}
|
||||
href={constructDataSourceExploreUrl(dataSource)}
|
||||
onClick={() => {
|
||||
trackExploreClicked({
|
||||
grafana_version: config.buildInfo.version,
|
||||
datasource_uid: dataSource.uid,
|
||||
plugin_name: dataSource.typeName,
|
||||
path: location.pathname,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Explore
|
||||
</LinkButton>
|
||||
)}
|
||||
</Card.Tags>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function DataSourcesListCardSkeleton({ hasExploreRights }: Pick<Props, 'hasExploreRights'>) {
|
||||
const skeletonStyles = useStyles2(getSkeletonStyles);
|
||||
return (
|
||||
<Card>
|
||||
<Card.Heading>
|
||||
<Skeleton width={140} />
|
||||
</Card.Heading>
|
||||
<Card.Figure>
|
||||
<Skeleton width={40} height={40} containerClassName={skeletonStyles.figure} />
|
||||
</Card.Figure>
|
||||
<Card.Meta>
|
||||
<Skeleton width={120} />
|
||||
</Card.Meta>
|
||||
<Card.Tags>
|
||||
<Stack direction="row" gap={2}>
|
||||
<Skeleton height={32} width={179} containerClassName={skeletonStyles.button} />
|
||||
|
||||
{/* Explore */}
|
||||
{hasExploreRights && <Skeleton height={32} width={107} containerClassName={skeletonStyles.button} />}
|
||||
</Stack>
|
||||
</Card.Tags>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
DataSourcesListCard.Skeleton = DataSourcesListCardSkeleton;
|
||||
|
||||
const getSkeletonStyles = () => {
|
||||
return {
|
||||
button: css({
|
||||
lineHeight: 1,
|
||||
}),
|
||||
figure: css({
|
||||
lineHeight: 1,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
logo: css({
|
||||
objectFit: 'contain',
|
||||
}),
|
||||
button: css({
|
||||
marginLeft: theme.spacing(2),
|
||||
}),
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue
Block a user