diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts index 456665e4f59..b06748151fe 100644 --- a/packages/grafana-data/src/index.ts +++ b/packages/grafana-data/src/index.ts @@ -19,6 +19,7 @@ export { BasicValueMatcherOptions, RangeValueMatcherOptions, } from './transformations/matchers/valueMatchers/types'; +export { LayoutModes, LayoutMode } from './types/layout'; export { PanelPlugin, SetFieldConfigOptionsArgs, StandardOptionConfig } from './panel/PanelPlugin'; export { createFieldConfigRegistry } from './panel/registryFactories'; export { QueryRunner, QueryRunnerOptions } from './types/queryRunner'; diff --git a/packages/grafana-data/src/types/layout.ts b/packages/grafana-data/src/types/layout.ts new file mode 100644 index 00000000000..4249dc34e60 --- /dev/null +++ b/packages/grafana-data/src/types/layout.ts @@ -0,0 +1,6 @@ +export type LayoutMode = LayoutModes.Grid | LayoutModes.List; + +export enum LayoutModes { + Grid = 'grid', + List = 'list', +} diff --git a/packages/grafana-ui/src/themes/GlobalStyles/GlobalStyles.tsx b/packages/grafana-ui/src/themes/GlobalStyles/GlobalStyles.tsx index e6209e27fe7..9a04f9fb568 100644 --- a/packages/grafana-ui/src/themes/GlobalStyles/GlobalStyles.tsx +++ b/packages/grafana-ui/src/themes/GlobalStyles/GlobalStyles.tsx @@ -2,11 +2,13 @@ import React from 'react'; import { Global } from '@emotion/react'; import { useTheme2 } from '..'; import { getElementStyles } from './elements'; +import { getCardStyles } from './card'; /** @internal */ export function GlobalStyles() { const theme = useTheme2(); const types = getElementStyles(theme); + const cards = getCardStyles(theme); - return ; + return ; } diff --git a/packages/grafana-ui/src/themes/GlobalStyles/card.tsx b/packages/grafana-ui/src/themes/GlobalStyles/card.tsx new file mode 100644 index 00000000000..74ce61c1ee6 --- /dev/null +++ b/packages/grafana-ui/src/themes/GlobalStyles/card.tsx @@ -0,0 +1,187 @@ +import { css } from '@emotion/react'; +import { GrafanaThemeV2 } from '@grafana/data'; +export function getCardStyles(theme: GrafanaThemeV2) { + return css` + .card-section { + margin-bottom: ${theme.spacing(4)}; + } + + .card-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + list-style-type: none; + } + + .card-item { + display: block; + height: 100%; + background: ${theme.colors.background.secondary}; + box-shadow: none; + padding: ${theme.spacing(2)}; + border-radius: 4px; + + &:hover { + background: ${theme.colors.emphasize(theme.colors.background.secondary, 0.03)}; + } + + .label-tag { + margin-left: ${theme.spacing(1)}; + font-size: 11px; + padding: 2px 6px; + } + } + + .card-item-body { + display: flex; + overflow: hidden; + } + + .card-item-details { + overflow: hidden; + } + + .card-item-header { + margin-bottom: ${theme.spacing(2)}; + } + + .card-item-type { + color: ${theme.colors.text.secondary}; + text-transform: uppercase; + font-size: ${theme.typography.size.sm}; + font-weight: ${theme.typography.fontWeightMedium}; + } + + .card-item-badge { + margin: 6px 0; + } + + .card-item-notice { + font-size: ${theme.typography.size.sm}; + } + + .card-item-name { + color: ${theme.colors.text.primary}; + overflow: hidden; + text-overflow: ellipsis; + width: 100%; + } + + .card-item-label { + margin-left: ${theme.spacing(1)}; + } + + .card-item-sub-name { + color: ${theme.colors.text.secondary}; + overflow: hidden; + text-overflow: ellipsis; + width: 100%; + } + + .card-item-sub-name--header { + color: ${theme.colors.text.secondary}; + text-transform: uppercase; + margin-bottom: ${theme.spacing(2)}; + font-size: ${theme.typography.size.sm}; + font-weight: bold; + } + + .card-list-layout-grid { + .card-item-type { + display: inline-block; + } + + .card-item-notice { + font-size: ${theme.typography.size.sm}; + display: inline-block; + margin-left: ${theme.spacing(2)}; + } + + .card-item-header-action { + float: right; + } + + .card-item-wrapper { + width: 100%; + padding: ${theme.spacing(0, 2, 2, 0)}; + } + + .card-item-wrapper--clickable { + cursor: pointer; + } + + .card-item-figure { + margin: ${theme.spacing(0, 2, 2, 0)}0; + height: 80px; + + img { + width: 80px; + } + } + + .card-item-name { + font-size: ${theme.typography.h3.fontSize}; + } + + ${theme.breakpoints.up('md')} { + .card-item-wrapper { + width: 50%; + } + } + + ${theme.breakpoints.up('lg')} { + .card-item-wrapper { + width: 33.333333%; + } + } + + &.card-list-layout-grid--max-2-col { + ${theme.breakpoints.up('lg')} { + .card-item-wrapper { + width: 50%; + } + } + } + } + + .card-list-layout-list { + .card-item-wrapper { + padding: 0; + width: 100%; + margin-bottom: ${theme.spacing(4)}; + } + + .card-item-wrapper--clickable { + cursor: pointer; + } + + .card-item { + border-radius: 2px; + } + + .card-item-header { + float: right; + text-align: right; + } + + .card-item-figure { + margin: ${theme.spacing(0, 2, 0, 0)}; + img { + width: 48px; + } + } + + .card-item-name { + font-size: ${theme.typography.h4.fontSize}; + } + + .card-item-sub-name { + font-size: ${theme.typography.size.sm}; + } + + .layout-selector { + margin-right: 0; + } + } + `; +} diff --git a/public/app/core/components/LayoutSelector/LayoutSelector.tsx b/public/app/core/components/LayoutSelector/LayoutSelector.tsx deleted file mode 100644 index 2633ef8838d..00000000000 --- a/public/app/core/components/LayoutSelector/LayoutSelector.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { FC } from 'react'; -import { RadioButtonGroup } from '@grafana/ui'; - -export type LayoutMode = LayoutModes.Grid | LayoutModes.List; - -export enum LayoutModes { - Grid = 'grid', - List = 'list', -} - -interface Props { - mode: LayoutMode; - onLayoutModeChanged: (mode: LayoutMode) => {}; -} - -const options = [ - { icon: 'table', value: LayoutModes.Grid }, - { icon: 'list-ul', value: LayoutModes.List }, -]; - -const LayoutSelector: FC = ({ mode, onLayoutModeChanged }) => ( -
- -
-); - -export default LayoutSelector; diff --git a/public/app/features/datasources/DataSourceList.test.tsx b/public/app/features/datasources/DataSourceList.test.tsx index 0cbe041b949..c6752e996d1 100644 --- a/public/app/features/datasources/DataSourceList.test.tsx +++ b/public/app/features/datasources/DataSourceList.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import DataSourcesList from './DataSourcesList'; import { getMockDataSources } from './__mocks__/dataSourcesMocks'; -import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector'; +import { LayoutModes } from '@grafana/data'; const setup = () => { const props = { diff --git a/public/app/features/datasources/DataSourcesList.tsx b/public/app/features/datasources/DataSourcesList.tsx index ee37265c022..c7c53538d36 100644 --- a/public/app/features/datasources/DataSourcesList.tsx +++ b/public/app/features/datasources/DataSourcesList.tsx @@ -2,8 +2,7 @@ import React, { FC } from 'react'; // Types -import { DataSourceSettings } from '@grafana/data'; -import { LayoutMode } from '../../core/components/LayoutSelector/LayoutSelector'; +import { DataSourceSettings, LayoutMode } from '@grafana/data'; import { Card, Tag, useStyles } from '@grafana/ui'; import { css } from '@emotion/css'; diff --git a/public/app/features/datasources/DataSourcesListPage.test.tsx b/public/app/features/datasources/DataSourcesListPage.test.tsx index e6735cd9816..bff292de3f3 100644 --- a/public/app/features/datasources/DataSourcesListPage.test.tsx +++ b/public/app/features/datasources/DataSourcesListPage.test.tsx @@ -1,9 +1,8 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { DataSourceSettings, NavModel } from '@grafana/data'; +import { DataSourceSettings, NavModel, LayoutModes } from '@grafana/data'; import { DataSourcesListPage, Props } from './DataSourcesListPage'; -import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector'; import { getMockDataSources } from './__mocks__/dataSourcesMocks'; import { setDataSourcesLayoutMode, setDataSourcesSearchQuery } from './state/reducers'; diff --git a/public/app/features/datasources/DataSourcesListPage.tsx b/public/app/features/datasources/DataSourcesListPage.tsx index 1a9d88e0e50..77ccb062f6d 100644 --- a/public/app/features/datasources/DataSourcesListPage.tsx +++ b/public/app/features/datasources/DataSourcesListPage.tsx @@ -8,10 +8,9 @@ import PageActionBar from 'app/core/components/PageActionBar/PageActionBar'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import DataSourcesList from './DataSourcesList'; // Types -import { DataSourceSettings, NavModel } from '@grafana/data'; +import { DataSourceSettings, NavModel, LayoutMode } from '@grafana/data'; import { IconName } from '@grafana/ui'; import { StoreState } from 'app/types'; -import { LayoutMode } from 'app/core/components/LayoutSelector/LayoutSelector'; // Actions import { loadDataSources } from './state/actions'; import { getNavModel } from 'app/core/selectors/navModel'; diff --git a/public/app/features/datasources/state/reducers.test.ts b/public/app/features/datasources/state/reducers.test.ts index 3f8094a8aa7..8cc844d86e6 100644 --- a/public/app/features/datasources/state/reducers.test.ts +++ b/public/app/features/datasources/state/reducers.test.ts @@ -18,9 +18,8 @@ import { setIsDefault, } from './reducers'; import { getMockDataSource, getMockDataSources } from '../__mocks__/dataSourcesMocks'; -import { LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector'; import { DataSourceSettingsState, DataSourcesState } from 'app/types'; -import { PluginMeta, PluginMetaInfo, PluginType } from '@grafana/data'; +import { PluginMeta, PluginMetaInfo, PluginType, LayoutModes } from '@grafana/data'; import { GenericDataSourcePlugin } from '../settings/PluginSettings'; const mockPlugin = () => diff --git a/public/app/features/datasources/state/reducers.ts b/public/app/features/datasources/state/reducers.ts index 0a7c6277cb4..44fe7998bee 100644 --- a/public/app/features/datasources/state/reducers.ts +++ b/public/app/features/datasources/state/reducers.ts @@ -1,8 +1,7 @@ import { AnyAction, createAction } from '@reduxjs/toolkit'; -import { DataSourcePluginMeta, DataSourceSettings } from '@grafana/data'; +import { DataSourcePluginMeta, DataSourceSettings, LayoutMode, LayoutModes } from '@grafana/data'; import { DataSourcesState, DataSourceSettingsState, TestingStatus } from 'app/types'; -import { LayoutMode, LayoutModes } from 'app/core/components/LayoutSelector/LayoutSelector'; import { DataSourceTypesLoadedPayload } from './actions'; import { GenericDataSourcePlugin } from '../settings/PluginSettings'; diff --git a/public/app/features/plugins/PluginList.test.tsx b/public/app/features/plugins/PluginList.test.tsx index 201dd69b9db..2f145706cd7 100644 --- a/public/app/features/plugins/PluginList.test.tsx +++ b/public/app/features/plugins/PluginList.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import PluginList from './PluginList'; import { getMockPlugins } from './__mocks__/pluginMocks'; -import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector'; +import { LayoutModes } from '@grafana/data'; const setup = (propOverrides?: object) => { const props = Object.assign( diff --git a/public/app/types/datasources.ts b/public/app/types/datasources.ts index a434cf657b1..589624a1f7d 100644 --- a/public/app/types/datasources.ts +++ b/public/app/types/datasources.ts @@ -1,5 +1,4 @@ -import { LayoutMode } from '../core/components/LayoutSelector/LayoutSelector'; -import { DataSourcePluginMeta, DataSourceSettings } from '@grafana/data'; +import { DataSourcePluginMeta, DataSourceSettings, LayoutMode } from '@grafana/data'; import { GenericDataSourcePlugin } from 'app/features/datasources/settings/PluginSettings'; import { HealthCheckResultDetails } from '@grafana/runtime/src/utils/DataSourceWithBackend'; diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 392310d631c..1c7ee51d8b7 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -34,7 +34,6 @@ // COMPONENTS @import '../app/features/dashboard/components/AddPanelWidget/AddPanelWidget'; @import 'components/scrollbar'; -@import 'components/cards'; @import 'components/buttons'; @import 'components/navs'; @import 'components/tabs'; diff --git a/public/sass/components/_cards.scss b/public/sass/components/_cards.scss deleted file mode 100644 index 44c56ccc4e7..00000000000 --- a/public/sass/components/_cards.scss +++ /dev/null @@ -1,217 +0,0 @@ -.layout-selector { - @include clearfix(); - - margin-left: $space-md; - text-align: right; - - button { - background: $input-label-bg; - color: $text-color-weak; - box-shadow: $card-shadow; - border: none; - padding: $space-sm; - line-height: 1; - font-size: 130%; - float: right; - - &:focus { - outline: none; - } - - &.active { - background-color: lighten($input-label-bg, 5%); - color: $link-color; - } - - &:nth-child(2) { - border-radius: 3px 0 0 3px; - border-right: $panel-border; - } - - &:nth-child(1) { - border-radius: 0 3px 3px 0; - } - } -} - -.card-section { - margin-bottom: $space-xl; -} - -.card-list { - display: flex; - flex-direction: row; - flex-wrap: wrap; - list-style-type: none; -} - -.card-item { - display: block; - height: 100%; - background: $card-background; - box-shadow: $card-shadow; - padding: $space-md; - border-radius: 4px; - - &:hover { - background: $card-background-hover; - } - - .label-tag { - margin-left: $space-sm; - font-size: 11px; - padding: 2px 6px; - } -} - -.card-item-body { - display: flex; - overflow: hidden; -} - -.card-item-details { - overflow: hidden; -} - -.card-item-header { - margin-bottom: $space-md; -} - -.card-item-type { - color: $text-color-weak; - text-transform: uppercase; - font-size: $font-size-sm; - font-weight: $font-weight-semi-bold; -} - -.card-item-badge { - margin: 6px 0; -} - -.card-item-notice { - font-size: $font-size-sm; -} - -.card-item-name { - color: $headings-color; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; -} - -.card-item-label { - margin-left: $space-sm; -} - -.card-item-sub-name { - color: $text-color-weak; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; -} - -.card-item-sub-name--header { - color: $text-color-weak; - text-transform: uppercase; - margin-bottom: $space-md; - font-size: $font-size-sm; - font-weight: bold; -} - -.card-list-layout-grid { - .card-item-type { - display: inline-block; - } - - .card-item-notice { - font-size: $font-size-sm; - display: inline-block; - margin-left: $space-md; - } - - .card-item-header-action { - float: right; - } - - .card-item-wrapper { - width: 100%; - padding: 0 $space-md $space-md 0; - } - - .card-item-wrapper--clickable { - cursor: pointer; - } - - .card-item-figure { - margin: 0 $space-md $space-md 0; - height: 80px; - - img { - width: 80px; - } - } - - .card-item-name { - font-size: $font-size-h3; - } - - @include media-breakpoint-up(md) { - .card-item-wrapper { - width: 50%; - } - } - - @include media-breakpoint-up(lg) { - .card-item-wrapper { - width: 33.333333%; - } - } - - &.card-list-layout-grid--max-2-col { - @include media-breakpoint-up(lg) { - .card-item-wrapper { - width: 50%; - } - } - } -} - -.card-list-layout-list { - .card-item-wrapper { - padding: 0; - width: 100%; - margin-bottom: $space-xs; - } - - .card-item-wrapper--clickable { - cursor: pointer; - } - - .card-item { - border-radius: 2px; - } - - .card-item-header { - float: right; - text-align: right; - } - - .card-item-figure { - margin: 0 $space-md 0 0; - img { - width: 48px; - } - } - - .card-item-name { - font-size: $font-size-h4; - } - - .card-item-sub-name { - font-size: $font-size-sm; - } - - .layout-selector { - margin-right: 0; - } -}