mirror of
https://github.com/grafana/grafana.git
synced 2026-09-05 04:40:13 -05:00
Pages: update react components to use v2 theme (#33413)
* chore: expose theme types / functions * fix(grafana-ui): withTheme2 extends themeable2 * feat: migrate page components to use new theme * refactor(pages): replace legacy form components with latest form components * refactor(dashboardimport): update page component to use theme spacing * refactor(alerting-ng): update page component to use v2 theme * test(dashboardpage): update test for v2 theme * test(apikeyspage): update test to select InlineSwitch component * test(createteam): update snapshot * refactor(playlist): update page components to use v2 theme * refactor(page): put back classes on page-container and background colors
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
export { createTheme } from './createTheme';
|
export { createTheme } from './createTheme';
|
||||||
export { ThemeRichColor, GrafanaThemeV2 } from './types';
|
export { ThemeRichColor, GrafanaThemeV2 } from './types';
|
||||||
export { ThemeColors } from './createColors';
|
export { ThemeColors } from './createColors';
|
||||||
export { ThemeBreakpoints } from './breakpoints';
|
export { ThemeBreakpoints, ThemeBreakpointsKey } from './breakpoints';
|
||||||
export { ThemeShadows } from './createShadows';
|
export { ThemeShadows } from './createShadows';
|
||||||
export { ThemeShape } from './createShape';
|
export { ThemeShape } from './createShape';
|
||||||
export { ThemeTypography, ThemeTypographyVariant } from './createTypography';
|
export { ThemeTypography, ThemeTypographyVariant } from './createTypography';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createTheme, GrafanaTheme, GrafanaThemeV2 } from '@grafana/data';
|
import { createTheme, GrafanaTheme, GrafanaThemeV2 } from '@grafana/data';
|
||||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
import hoistNonReactStatics from 'hoist-non-react-statics';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { Themeable } from '../types/theme';
|
import { Themeable, Themeable2 } from '../types/theme';
|
||||||
import { stylesFactory } from './stylesFactory';
|
import { stylesFactory } from './stylesFactory';
|
||||||
|
|
||||||
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
|
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
|
||||||
@@ -42,8 +42,8 @@ export const withTheme = <P extends Themeable, S extends {} = {}>(Component: Rea
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** @alpha */
|
/** @alpha */
|
||||||
export const withTheme2 = <P extends Themeable, S extends {} = {}>(Component: React.ComponentType<P>) => {
|
export const withTheme2 = <P extends Themeable2, S extends {} = {}>(Component: React.ComponentType<P>) => {
|
||||||
const WithTheme: React.FunctionComponent<Subtract<P, Themeable>> = (props) => {
|
const WithTheme: React.FunctionComponent<Subtract<P, Themeable2>> = (props) => {
|
||||||
/**
|
/**
|
||||||
* If theme context is mocked, let's use it instead of the original context
|
* If theme context is mocked, let's use it instead of the original context
|
||||||
* This is used in tests when mocking theme using mockThemeContext function defined below
|
* This is used in tests when mocking theme using mockThemeContext function defined below
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
export { ThemeContext, withTheme, useTheme, useTheme2, useStyles, useStyles2, mockThemeContext } from './ThemeContext';
|
export {
|
||||||
|
ThemeContext,
|
||||||
|
withTheme,
|
||||||
|
withTheme2,
|
||||||
|
useTheme,
|
||||||
|
useTheme2,
|
||||||
|
useStyles,
|
||||||
|
useStyles2,
|
||||||
|
mockThemeContext,
|
||||||
|
} from './ThemeContext';
|
||||||
export { getTheme, mockTheme } from './getTheme';
|
export { getTheme, mockTheme } from './getTheme';
|
||||||
export { stylesFactory } from './stylesFactory';
|
export { stylesFactory } from './stylesFactory';
|
||||||
export { GlobalStyles } from './GlobalStyles/GlobalStyles';
|
export { GlobalStyles } from './GlobalStyles/GlobalStyles';
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ import { getTitleFromNavModel } from 'app/core/selectors/navModel';
|
|||||||
import PageHeader from '../PageHeader/PageHeader';
|
import PageHeader from '../PageHeader/PageHeader';
|
||||||
import { Footer } from '../Footer/Footer';
|
import { Footer } from '../Footer/Footer';
|
||||||
import { PageContents } from './PageContents';
|
import { PageContents } from './PageContents';
|
||||||
import { CustomScrollbar, useStyles } from '@grafana/ui';
|
import { CustomScrollbar, useStyles2 } from '@grafana/ui';
|
||||||
import { GrafanaTheme, NavModel } from '@grafana/data';
|
import { GrafanaThemeV2, NavModel, ThemeBreakpointsKey } from '@grafana/data';
|
||||||
import { Branding } from '../Branding/Branding';
|
import { Branding } from '../Branding/Branding';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
|
|
||||||
interface Props extends HTMLAttributes<HTMLDivElement> {
|
interface Props extends HTMLAttributes<HTMLDivElement> {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
navModel: NavModel;
|
navModel: NavModel;
|
||||||
contentWidth?: keyof GrafanaTheme['breakpoints'];
|
contentWidth?: ThemeBreakpointsKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageType extends FC<Props> {
|
export interface PageType extends FC<Props> {
|
||||||
@@ -23,7 +23,7 @@ export interface PageType extends FC<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Page: PageType = ({ navModel, children, className, contentWidth, ...otherProps }) => {
|
export const Page: PageType = ({ navModel, children, className, contentWidth, ...otherProps }) => {
|
||||||
const styles = useStyles(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const title = getTitleFromNavModel(navModel);
|
const title = getTitleFromNavModel(navModel);
|
||||||
@@ -51,17 +51,17 @@ Page.Contents = PageContents;
|
|||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme) => ({
|
const getStyles = (theme: GrafanaThemeV2) => ({
|
||||||
wrapper: css`
|
wrapper: css`
|
||||||
|
background: ${theme.colors.background.primary};
|
||||||
|
bottom: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: ${theme.colors.bg1};
|
|
||||||
`,
|
`,
|
||||||
contentWidth: (size: keyof GrafanaTheme['breakpoints']) => css`
|
contentWidth: (size: ThemeBreakpointsKey) => css`
|
||||||
.page-container {
|
.page-container {
|
||||||
max-width: ${theme.breakpoints[size]};
|
max-width: ${theme.breakpoints.values[size]}px;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import React, { FormEvent, PureComponent } from 'react';
|
|||||||
import { hot } from 'react-hot-loader';
|
import { hot } from 'react-hot-loader';
|
||||||
import { connect, ConnectedProps } from 'react-redux';
|
import { connect, ConnectedProps } from 'react-redux';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { GrafanaTheme, SelectableValue } from '@grafana/data';
|
import { GrafanaThemeV2, SelectableValue } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { PageToolbar, stylesFactory, ToolbarButton } from '@grafana/ui';
|
import { PageToolbar, stylesFactory, ToolbarButton, withTheme2, Themeable2 } from '@grafana/ui';
|
||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper';
|
import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper';
|
||||||
import { AlertingQueryEditor } from './components/AlertingQueryEditor';
|
import { AlertingQueryEditor } from './components/AlertingQueryEditor';
|
||||||
@@ -48,13 +48,13 @@ const connector = connect(mapStateToProps, mapDispatchToProps);
|
|||||||
|
|
||||||
interface RouteProps extends GrafanaRouteComponentProps<{ id: string }> {}
|
interface RouteProps extends GrafanaRouteComponentProps<{ id: string }> {}
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps extends Themeable2 {
|
||||||
saveDefinition: typeof createAlertDefinition | typeof updateAlertDefinition;
|
saveDefinition: typeof createAlertDefinition | typeof updateAlertDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = OwnProps & ConnectedProps<typeof connector>;
|
type Props = OwnProps & ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
class NextGenAlertingPageUnconnected extends PureComponent<Props> {
|
class UnthemedNextGenAlertingPage extends PureComponent<Props> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { getAlertDefinition, pageId } = this.props;
|
const { getAlertDefinition, pageId } = this.props;
|
||||||
|
|
||||||
@@ -122,9 +122,9 @@ class NextGenAlertingPageUnconnected extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { alertDefinition, uiState, updateAlertDefinitionUiState, getInstances } = this.props;
|
const { alertDefinition, uiState, updateAlertDefinitionUiState, getInstances, theme } = this.props;
|
||||||
|
|
||||||
const styles = getStyles(config.theme);
|
const styles = getStyles(theme);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
@@ -154,16 +154,18 @@ class NextGenAlertingPageUnconnected extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NextGenAlertingPageUnconnected = withTheme2(UnthemedNextGenAlertingPage);
|
||||||
|
|
||||||
export default hot(module)(connector(NextGenAlertingPageUnconnected));
|
export default hot(module)(connector(NextGenAlertingPageUnconnected));
|
||||||
|
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
|
const getStyles = stylesFactory((theme: GrafanaThemeV2) => ({
|
||||||
wrapper: css`
|
wrapper: css`
|
||||||
width: calc(100% - 55px);
|
width: calc(100% - 55px);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: ${theme.colors.dashboardBg};
|
background: ${theme.colors.background.canvas};
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -187,8 +187,8 @@ describe('ApiKeysPage', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function toggleShowExpired() {
|
function toggleShowExpired() {
|
||||||
expect(screen.getByText(/show expired/i)).toBeInTheDocument();
|
expect(screen.queryByLabelText(/show expired/i)).toBeInTheDocument();
|
||||||
userEvent.click(screen.getByText(/show expired/i));
|
userEvent.click(screen.getByLabelText(/show expired/i));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addAndVerifyApiKey(addApiKeyMock: jest.Mock, includeExpired: boolean) {
|
async function addAndVerifyApiKey(addApiKeyMock: jest.Mock, includeExpired: boolean) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { ApiKeysAddedModal } from './ApiKeysAddedModal';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||||
import { LegacyForms } from '@grafana/ui';
|
import { InlineField, InlineSwitch } from '@grafana/ui';
|
||||||
import { rangeUtil } from '@grafana/data';
|
import { rangeUtil } from '@grafana/data';
|
||||||
import { getTimeZone } from 'app/features/profile/state/selectors';
|
import { getTimeZone } from 'app/features/profile/state/selectors';
|
||||||
import { setSearchQuery } from './state/reducers';
|
import { setSearchQuery } from './state/reducers';
|
||||||
@@ -21,8 +21,6 @@ import { ApiKeysTable } from './ApiKeysTable';
|
|||||||
import { ApiKeysController } from './ApiKeysController';
|
import { ApiKeysController } from './ApiKeysController';
|
||||||
import { ShowModalReactEvent } from 'app/types/events';
|
import { ShowModalReactEvent } from 'app/types/events';
|
||||||
|
|
||||||
const { Switch } = LegacyForms;
|
|
||||||
|
|
||||||
function mapStateToProps(state: StoreState) {
|
function mapStateToProps(state: StoreState) {
|
||||||
return {
|
return {
|
||||||
navModel: getNavModel(state.navIndex, 'apikeys'),
|
navModel: getNavModel(state.navIndex, 'apikeys'),
|
||||||
@@ -154,7 +152,9 @@ export class ApiKeysPageUnconnected extends PureComponent<Props, State> {
|
|||||||
{showTable ? (
|
{showTable ? (
|
||||||
<>
|
<>
|
||||||
<h3 className="page-heading">Existing API keys</h3>
|
<h3 className="page-heading">Existing API keys</h3>
|
||||||
<Switch label="Show expired" checked={includeExpired} onChange={this.onIncludeExpiredChange} />
|
<InlineField label="Show expired">
|
||||||
|
<InlineSwitch id="showExpired" value={includeExpired} onChange={this.onIncludeExpiredChange} />
|
||||||
|
</InlineField>
|
||||||
<ApiKeysTable apiKeys={apiKeys} timeZone={timeZone} onDelete={this.onDeleteApiKey} />
|
<ApiKeysTable apiKeys={apiKeys} timeZone={timeZone} onDelete={this.onDeleteApiKey} />
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { notifyApp } from 'app/core/actions';
|
|||||||
import { cleanUpDashboardAndVariables } from '../state/actions';
|
import { cleanUpDashboardAndVariables } from '../state/actions';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
|
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
|
||||||
import { getTheme } from '@grafana/ui';
|
import { createTheme } from '@grafana/data';
|
||||||
|
|
||||||
jest.mock('app/features/dashboard/components/DashboardSettings/GeneralSettings', () => ({}));
|
jest.mock('app/features/dashboard/components/DashboardSettings/GeneralSettings', () => ({}));
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioCo
|
|||||||
cancelVariables: jest.fn(),
|
cancelVariables: jest.fn(),
|
||||||
templateVarsChangedInUrl: jest.fn(),
|
templateVarsChangedInUrl: jest.fn(),
|
||||||
dashboard: null,
|
dashboard: null,
|
||||||
theme: getTheme(),
|
theme: createTheme(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(props, propOverrides);
|
Object.assign(props, propOverrides);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { hot } from 'react-hot-loader';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { CustomScrollbar, stylesFactory, Themeable, withTheme } from '@grafana/ui';
|
import { CustomScrollbar, stylesFactory, Themeable2, withTheme2 } from '@grafana/ui';
|
||||||
|
|
||||||
import { createErrorNotification } from 'app/core/copy/appNotification';
|
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||||
import { Branding } from 'app/core/components/Branding/Branding';
|
import { Branding } from 'app/core/components/Branding/Branding';
|
||||||
@@ -26,7 +26,7 @@ import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
|||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||||
import { getTimeSrv } from '../services/TimeSrv';
|
import { getTimeSrv } from '../services/TimeSrv';
|
||||||
import { getKioskMode } from 'app/core/navigation/kiosk';
|
import { getKioskMode } from 'app/core/navigation/kiosk';
|
||||||
import { GrafanaTheme, UrlQueryValue } from '@grafana/data';
|
import { GrafanaThemeV2, UrlQueryValue } from '@grafana/data';
|
||||||
import { DashboardLoading } from '../components/DashboardLoading/DashboardLoading';
|
import { DashboardLoading } from '../components/DashboardLoading/DashboardLoading';
|
||||||
import { DashboardFailed } from '../components/DashboardLoading/DashboardFailed';
|
import { DashboardFailed } from '../components/DashboardLoading/DashboardFailed';
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ type DashboardPageRouteSearchParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface Props
|
export interface Props
|
||||||
extends Themeable,
|
extends Themeable2,
|
||||||
GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams> {
|
GrafanaRouteComponentProps<DashboardPageRouteParams, DashboardPageRouteSearchParams> {
|
||||||
initPhase: DashboardInitPhase;
|
initPhase: DashboardInitPhase;
|
||||||
isInitSlow: boolean;
|
isInitSlow: boolean;
|
||||||
@@ -373,7 +373,7 @@ const mapDispatchToProps = {
|
|||||||
/*
|
/*
|
||||||
* Styles
|
* Styles
|
||||||
*/
|
*/
|
||||||
export const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
export const getStyles = stylesFactory((theme: GrafanaThemeV2) => {
|
||||||
return {
|
return {
|
||||||
dashboardContainer: css`
|
dashboardContainer: css`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -392,13 +392,13 @@ export const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
`,
|
`,
|
||||||
dashboardContent: css`
|
dashboardContent: css`
|
||||||
padding: ${theme.spacing.md};
|
padding: ${theme.spacing(2)};
|
||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DashboardPage = withTheme(UnthemedDashboardPage);
|
export const DashboardPage = withTheme2(UnthemedDashboardPage);
|
||||||
DashboardPage.displayName = 'DashboardPage';
|
DashboardPage.displayName = 'DashboardPage';
|
||||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));
|
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
import React, { FormEvent, PureComponent } from 'react';
|
import React, { FormEvent, PureComponent } from 'react';
|
||||||
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
|
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { AppEvents, NavModel } from '@grafana/data';
|
import { AppEvents, GrafanaThemeV2, NavModel } from '@grafana/data';
|
||||||
import { Button, stylesFactory, Input, TextArea, Field, Form, Legend, FileUpload } from '@grafana/ui';
|
import {
|
||||||
|
Button,
|
||||||
|
stylesFactory,
|
||||||
|
withTheme2,
|
||||||
|
Input,
|
||||||
|
TextArea,
|
||||||
|
Field,
|
||||||
|
Form,
|
||||||
|
Legend,
|
||||||
|
FileUpload,
|
||||||
|
Themeable2,
|
||||||
|
} from '@grafana/ui';
|
||||||
import Page from 'app/core/components/Page/Page';
|
import Page from 'app/core/components/Page/Page';
|
||||||
import { connectWithCleanUp } from 'app/core/components/connectWithCleanUp';
|
import { connectWithCleanUp } from 'app/core/components/connectWithCleanUp';
|
||||||
import { ImportDashboardOverview } from './components/ImportDashboardOverview';
|
import { ImportDashboardOverview } from './components/ImportDashboardOverview';
|
||||||
@@ -12,7 +23,7 @@ import appEvents from 'app/core/app_events';
|
|||||||
import { getNavModel } from 'app/core/selectors/navModel';
|
import { getNavModel } from 'app/core/selectors/navModel';
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
|
|
||||||
interface OwnProps {}
|
interface OwnProps extends Themeable2 {}
|
||||||
|
|
||||||
interface ConnectedProps {
|
interface ConnectedProps {
|
||||||
navModel: NavModel;
|
navModel: NavModel;
|
||||||
@@ -26,7 +37,7 @@ interface DispatchProps {
|
|||||||
|
|
||||||
type Props = OwnProps & ConnectedProps & DispatchProps;
|
type Props = OwnProps & ConnectedProps & DispatchProps;
|
||||||
|
|
||||||
class DashboardImportUnConnected extends PureComponent<Props> {
|
class UnthemedDashboardImport extends PureComponent<Props> {
|
||||||
onFileUpload = (event: FormEvent<HTMLInputElement>) => {
|
onFileUpload = (event: FormEvent<HTMLInputElement>) => {
|
||||||
const { importDashboardJson } = this.props;
|
const { importDashboardJson } = this.props;
|
||||||
const file = event.currentTarget.files && event.currentTarget.files.length > 0 && event.currentTarget.files[0];
|
const file = event.currentTarget.files && event.currentTarget.files.length > 0 && event.currentTarget.files[0];
|
||||||
@@ -72,7 +83,7 @@ class DashboardImportUnConnected extends PureComponent<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderImportForm() {
|
renderImportForm() {
|
||||||
const styles = importStyles();
|
const styles = importStyles(this.props.theme);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -134,6 +145,8 @@ class DashboardImportUnConnected extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DashboardImportUnConnected = withTheme2(UnthemedDashboardImport);
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state: StoreState) => ({
|
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state: StoreState) => ({
|
||||||
navModel: getNavModel(state.navIndex, 'import', undefined, true),
|
navModel: getNavModel(state.navIndex, 'import', undefined, true),
|
||||||
isLoaded: state.importDashboard.isLoaded,
|
isLoaded: state.importDashboard.isLoaded,
|
||||||
@@ -154,10 +167,10 @@ export default DashboardImportPage;
|
|||||||
|
|
||||||
DashboardImportPage.displayName = 'DashboardImport';
|
DashboardImportPage.displayName = 'DashboardImport';
|
||||||
|
|
||||||
const importStyles = stylesFactory(() => {
|
const importStyles = stylesFactory((theme: GrafanaThemeV2) => {
|
||||||
return {
|
return {
|
||||||
option: css`
|
option: css`
|
||||||
margin-bottom: 32px;
|
margin-bottom: ${theme.spacing(4)};
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { FC } from 'react';
|
|||||||
import { connect, MapStateToProps } from 'react-redux';
|
import { connect, MapStateToProps } from 'react-redux';
|
||||||
import { NavModel } from '@grafana/data';
|
import { NavModel } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { useStyles } from '@grafana/ui';
|
import { useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
import Page from 'app/core/components/Page/Page';
|
import Page from 'app/core/components/Page/Page';
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
@@ -25,7 +25,7 @@ export interface RouteParams {
|
|||||||
interface Props extends ConnectedProps, GrafanaRouteComponentProps<RouteParams> {}
|
interface Props extends ConnectedProps, GrafanaRouteComponentProps<RouteParams> {}
|
||||||
|
|
||||||
export const PlaylistEditPage: FC<Props> = ({ navModel, match }) => {
|
export const PlaylistEditPage: FC<Props> = ({ navModel, match }) => {
|
||||||
const styles = useStyles(getPlaylistStyles);
|
const styles = useStyles2(getPlaylistStyles);
|
||||||
const { playlist, loading } = usePlaylist(match.params.id);
|
const { playlist, loading } = usePlaylist(match.params.id);
|
||||||
const onSubmit = async (playlist: Playlist) => {
|
const onSubmit = async (playlist: Playlist) => {
|
||||||
await updatePlaylist(match.params.id, playlist);
|
await updatePlaylist(match.params.id, playlist);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { FC } from 'react';
|
|||||||
import { connect, MapStateToProps } from 'react-redux';
|
import { connect, MapStateToProps } from 'react-redux';
|
||||||
import { NavModel } from '@grafana/data';
|
import { NavModel } from '@grafana/data';
|
||||||
import { locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { useStyles } from '@grafana/ui';
|
import { useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
import Page from 'app/core/components/Page/Page';
|
import Page from 'app/core/components/Page/Page';
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
@@ -21,7 +21,7 @@ interface ConnectedProps {
|
|||||||
interface Props extends ConnectedProps, GrafanaRouteComponentProps {}
|
interface Props extends ConnectedProps, GrafanaRouteComponentProps {}
|
||||||
|
|
||||||
export const PlaylistNewPage: FC<Props> = ({ navModel }) => {
|
export const PlaylistNewPage: FC<Props> = ({ navModel }) => {
|
||||||
const styles = useStyles(getPlaylistStyles);
|
const styles = useStyles2(getPlaylistStyles);
|
||||||
const { playlist, loading } = usePlaylist();
|
const { playlist, loading } = usePlaylist();
|
||||||
const onSubmit = async (playlist: Playlist) => {
|
const onSubmit = async (playlist: Playlist) => {
|
||||||
await createPlaylist(playlist);
|
await createPlaylist(playlist);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaThemeV2 } from '@grafana/data';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
export function getPlaylistStyles(theme: GrafanaTheme) {
|
export function getPlaylistStyles(theme: GrafanaThemeV2) {
|
||||||
return {
|
return {
|
||||||
description: css`
|
description: css`
|
||||||
label: description;
|
label: description;
|
||||||
@@ -10,7 +10,7 @@ export function getPlaylistStyles(theme: GrafanaTheme) {
|
|||||||
`,
|
`,
|
||||||
subHeading: css`
|
subHeading: css`
|
||||||
label: sub-heading;
|
label: sub-heading;
|
||||||
margin-bottom: ${theme.spacing.md};
|
margin-bottom: ${theme.spacing(2)};
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import Page from 'app/core/components/Page/Page';
|
import Page from 'app/core/components/Page/Page';
|
||||||
import { hot } from 'react-hot-loader';
|
import { hot } from 'react-hot-loader';
|
||||||
import { Button, LegacyForms } from '@grafana/ui';
|
import { Button, Form, Field, Input, FieldSet, Label, Tooltip, Icon } from '@grafana/ui';
|
||||||
const { FormField } = LegacyForms;
|
|
||||||
import { NavModel } from '@grafana/data';
|
import { NavModel } from '@grafana/data';
|
||||||
import { getBackendSrv, locationService } from '@grafana/runtime';
|
import { getBackendSrv, locationService } from '@grafana/runtime';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
@@ -13,78 +12,50 @@ export interface Props {
|
|||||||
navModel: NavModel;
|
navModel: NavModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface TeamDTO {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CreateTeam extends PureComponent<Props, State> {
|
export class CreateTeam extends PureComponent<Props> {
|
||||||
state: State = {
|
create = async (formModel: TeamDTO) => {
|
||||||
name: '',
|
const result = await getBackendSrv().post('/api/teams', formModel);
|
||||||
email: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
create = async (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const { name, email } = this.state;
|
|
||||||
|
|
||||||
const result = await getBackendSrv().post('/api/teams', { name, email });
|
|
||||||
if (result.teamId) {
|
if (result.teamId) {
|
||||||
locationService.push(`/org/teams/edit/${result.teamId}`);
|
locationService.push(`/org/teams/edit/${result.teamId}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.setState({
|
|
||||||
email: event.target.value,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.setState({
|
|
||||||
name: event.target.value,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { navModel } = this.props;
|
const { navModel } = this.props;
|
||||||
const { name, email } = this.state;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page navModel={navModel}>
|
<Page navModel={navModel}>
|
||||||
<Page.Contents>
|
<Page.Contents>
|
||||||
<>
|
<Form onSubmit={this.create}>
|
||||||
<h3 className="page-sub-heading">New Team</h3>
|
{({ register }) => (
|
||||||
|
<FieldSet label="New Team">
|
||||||
<form className="gf-form-group" onSubmit={this.create}>
|
<Field label="Name">
|
||||||
<FormField
|
<Input name="name" ref={register({ required: true })} width={60} />
|
||||||
className="gf-form"
|
</Field>
|
||||||
label="Name"
|
<Field
|
||||||
value={name}
|
label={
|
||||||
onChange={this.onNameChange}
|
<Label>
|
||||||
inputWidth={30}
|
<span>Email</span>
|
||||||
labelWidth={10}
|
<Tooltip content="This is optional and is primarily used for allowing custom team avatars.">
|
||||||
required
|
<Icon name="info-circle" style={{ marginLeft: 6 }} />
|
||||||
/>
|
</Tooltip>
|
||||||
<FormField
|
</Label>
|
||||||
type="email"
|
}
|
||||||
className="gf-form"
|
>
|
||||||
label="Email"
|
<Input type="email" name="email" ref={register()} placeholder="email@test.com" width={60} />
|
||||||
value={email}
|
</Field>
|
||||||
onChange={this.onEmailChange}
|
<div className="gf-form-button-row">
|
||||||
inputWidth={30}
|
<Button type="submit" variant="primary">
|
||||||
labelWidth={10}
|
Create
|
||||||
placeholder="email@test.com"
|
</Button>
|
||||||
tooltip="This is optional and is primarily used for allowing custom team avatars."
|
</div>
|
||||||
/>
|
</FieldSet>
|
||||||
<div className="gf-form-button-row">
|
)}
|
||||||
<Button type="submit" variant="primary">
|
</Form>
|
||||||
Create
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</>
|
|
||||||
</Page.Contents>
|
</Page.Contents>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,46 +5,11 @@ exports[`Render should render component 1`] = `
|
|||||||
navModel={Object {}}
|
navModel={Object {}}
|
||||||
>
|
>
|
||||||
<PageContents>
|
<PageContents>
|
||||||
<h3
|
<Form
|
||||||
className="page-sub-heading"
|
|
||||||
>
|
|
||||||
New Team
|
|
||||||
</h3>
|
|
||||||
<form
|
|
||||||
className="gf-form-group"
|
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
>
|
>
|
||||||
<FormField
|
<Component />
|
||||||
className="gf-form"
|
</Form>
|
||||||
inputWidth={30}
|
|
||||||
label="Name"
|
|
||||||
labelWidth={10}
|
|
||||||
onChange={[Function]}
|
|
||||||
required={true}
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
className="gf-form"
|
|
||||||
inputWidth={30}
|
|
||||||
label="Email"
|
|
||||||
labelWidth={10}
|
|
||||||
onChange={[Function]}
|
|
||||||
placeholder="email@test.com"
|
|
||||||
tooltip="This is optional and is primarily used for allowing custom team avatars."
|
|
||||||
type="email"
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="gf-form-button-row"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
variant="primary"
|
|
||||||
>
|
|
||||||
Create
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</PageContents>
|
</PageContents>
|
||||||
</Page>
|
</Page>
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user