diff --git a/packages/grafana-data/src/themes/index.ts b/packages/grafana-data/src/themes/index.ts index 7fe1114eedd..2899f55acee 100644 --- a/packages/grafana-data/src/themes/index.ts +++ b/packages/grafana-data/src/themes/index.ts @@ -1,7 +1,7 @@ export { createTheme } from './createTheme'; export { ThemeRichColor, GrafanaThemeV2 } from './types'; export { ThemeColors } from './createColors'; -export { ThemeBreakpoints } from './breakpoints'; +export { ThemeBreakpoints, ThemeBreakpointsKey } from './breakpoints'; export { ThemeShadows } from './createShadows'; export { ThemeShape } from './createShape'; export { ThemeTypography, ThemeTypographyVariant } from './createTypography'; diff --git a/packages/grafana-ui/src/themes/ThemeContext.tsx b/packages/grafana-ui/src/themes/ThemeContext.tsx index 38398ac41e6..646ca73c4ea 100644 --- a/packages/grafana-ui/src/themes/ThemeContext.tsx +++ b/packages/grafana-ui/src/themes/ThemeContext.tsx @@ -1,7 +1,7 @@ import { createTheme, GrafanaTheme, GrafanaThemeV2 } from '@grafana/data'; import hoistNonReactStatics from 'hoist-non-react-statics'; import React, { useContext } from 'react'; -import { Themeable } from '../types/theme'; +import { Themeable, Themeable2 } from '../types/theme'; import { stylesFactory } from './stylesFactory'; type Omit = Pick>; @@ -42,8 +42,8 @@ export const withTheme =

(Component: Rea }; /** @alpha */ -export const withTheme2 =

(Component: React.ComponentType

) => { - const WithTheme: React.FunctionComponent> = (props) => { +export const withTheme2 =

(Component: React.ComponentType

) => { + const WithTheme: React.FunctionComponent> = (props) => { /** * 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 diff --git a/packages/grafana-ui/src/themes/index.ts b/packages/grafana-ui/src/themes/index.ts index 4e25838144b..4be9327ffb6 100644 --- a/packages/grafana-ui/src/themes/index.ts +++ b/packages/grafana-ui/src/themes/index.ts @@ -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 { stylesFactory } from './stylesFactory'; export { GlobalStyles } from './GlobalStyles/GlobalStyles'; diff --git a/public/app/core/components/Page/Page.tsx b/public/app/core/components/Page/Page.tsx index f7ec6696c69..d13e67b75ea 100644 --- a/public/app/core/components/Page/Page.tsx +++ b/public/app/core/components/Page/Page.tsx @@ -6,15 +6,15 @@ import { getTitleFromNavModel } from 'app/core/selectors/navModel'; import PageHeader from '../PageHeader/PageHeader'; import { Footer } from '../Footer/Footer'; import { PageContents } from './PageContents'; -import { CustomScrollbar, useStyles } from '@grafana/ui'; -import { GrafanaTheme, NavModel } from '@grafana/data'; +import { CustomScrollbar, useStyles2 } from '@grafana/ui'; +import { GrafanaThemeV2, NavModel, ThemeBreakpointsKey } from '@grafana/data'; import { Branding } from '../Branding/Branding'; import { css, cx } from '@emotion/css'; interface Props extends HTMLAttributes { children: React.ReactNode; navModel: NavModel; - contentWidth?: keyof GrafanaTheme['breakpoints']; + contentWidth?: ThemeBreakpointsKey; } export interface PageType extends FC { @@ -23,7 +23,7 @@ export interface PageType extends FC { } export const Page: PageType = ({ navModel, children, className, contentWidth, ...otherProps }) => { - const styles = useStyles(getStyles); + const styles = useStyles2(getStyles); useEffect(() => { const title = getTitleFromNavModel(navModel); @@ -51,17 +51,17 @@ Page.Contents = PageContents; export default Page; -const getStyles = (theme: GrafanaTheme) => ({ +const getStyles = (theme: GrafanaThemeV2) => ({ wrapper: css` + background: ${theme.colors.background.primary}; + bottom: 0; position: absolute; top: 0; - bottom: 0; width: 100%; - background: ${theme.colors.bg1}; `, - contentWidth: (size: keyof GrafanaTheme['breakpoints']) => css` + contentWidth: (size: ThemeBreakpointsKey) => css` .page-container { - max-width: ${theme.breakpoints[size]}; + max-width: ${theme.breakpoints.values[size]}px; } `, }); diff --git a/public/app/features/alerting/NextGenAlertingPage.tsx b/public/app/features/alerting/NextGenAlertingPage.tsx index d1e13d68d26..5942ce082a9 100644 --- a/public/app/features/alerting/NextGenAlertingPage.tsx +++ b/public/app/features/alerting/NextGenAlertingPage.tsx @@ -2,9 +2,9 @@ import React, { FormEvent, PureComponent } from 'react'; import { hot } from 'react-hot-loader'; import { connect, ConnectedProps } from 'react-redux'; import { css } from '@emotion/css'; -import { GrafanaTheme, SelectableValue } from '@grafana/data'; +import { GrafanaThemeV2, SelectableValue } from '@grafana/data'; 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 { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper'; import { AlertingQueryEditor } from './components/AlertingQueryEditor'; @@ -48,13 +48,13 @@ const connector = connect(mapStateToProps, mapDispatchToProps); interface RouteProps extends GrafanaRouteComponentProps<{ id: string }> {} -interface OwnProps { +interface OwnProps extends Themeable2 { saveDefinition: typeof createAlertDefinition | typeof updateAlertDefinition; } type Props = OwnProps & ConnectedProps; -class NextGenAlertingPageUnconnected extends PureComponent { +class UnthemedNextGenAlertingPage extends PureComponent { componentDidMount() { const { getAlertDefinition, pageId } = this.props; @@ -122,9 +122,9 @@ class NextGenAlertingPageUnconnected extends PureComponent { } 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 (

@@ -154,16 +154,18 @@ class NextGenAlertingPageUnconnected extends PureComponent { } } +const NextGenAlertingPageUnconnected = withTheme2(UnthemedNextGenAlertingPage); + export default hot(module)(connector(NextGenAlertingPageUnconnected)); -const getStyles = stylesFactory((theme: GrafanaTheme) => ({ +const getStyles = stylesFactory((theme: GrafanaThemeV2) => ({ wrapper: css` width: calc(100% - 55px); height: 100%; position: fixed; top: 0; bottom: 0; - background: ${theme.colors.dashboardBg}; + background: ${theme.colors.background.canvas}; display: flex; flex-direction: column; `, diff --git a/public/app/features/api-keys/ApiKeysPage.test.tsx b/public/app/features/api-keys/ApiKeysPage.test.tsx index edf308e1fef..d81cc7a0511 100644 --- a/public/app/features/api-keys/ApiKeysPage.test.tsx +++ b/public/app/features/api-keys/ApiKeysPage.test.tsx @@ -187,8 +187,8 @@ describe('ApiKeysPage', () => { }); function toggleShowExpired() { - expect(screen.getByText(/show expired/i)).toBeInTheDocument(); - userEvent.click(screen.getByText(/show expired/i)); + expect(screen.queryByLabelText(/show expired/i)).toBeInTheDocument(); + userEvent.click(screen.getByLabelText(/show expired/i)); } async function addAndVerifyApiKey(addApiKeyMock: jest.Mock, includeExpired: boolean) { diff --git a/public/app/features/api-keys/ApiKeysPage.tsx b/public/app/features/api-keys/ApiKeysPage.tsx index f737aa6a217..6f1b05d9863 100644 --- a/public/app/features/api-keys/ApiKeysPage.tsx +++ b/public/app/features/api-keys/ApiKeysPage.tsx @@ -11,7 +11,7 @@ import { ApiKeysAddedModal } from './ApiKeysAddedModal'; import config from 'app/core/config'; import appEvents from 'app/core/app_events'; 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 { getTimeZone } from 'app/features/profile/state/selectors'; import { setSearchQuery } from './state/reducers'; @@ -21,8 +21,6 @@ import { ApiKeysTable } from './ApiKeysTable'; import { ApiKeysController } from './ApiKeysController'; import { ShowModalReactEvent } from 'app/types/events'; -const { Switch } = LegacyForms; - function mapStateToProps(state: StoreState) { return { navModel: getNavModel(state.navIndex, 'apikeys'), @@ -154,7 +152,9 @@ export class ApiKeysPageUnconnected extends PureComponent { {showTable ? ( <>

Existing API keys

- + + + ) : null} diff --git a/public/app/features/dashboard/containers/DashboardPage.test.tsx b/public/app/features/dashboard/containers/DashboardPage.test.tsx index 04371b19b12..cebc986639a 100644 --- a/public/app/features/dashboard/containers/DashboardPage.test.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.test.tsx @@ -8,7 +8,7 @@ import { notifyApp } from 'app/core/actions'; import { cleanUpDashboardAndVariables } from '../state/actions'; import { selectors } from '@grafana/e2e-selectors'; 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', () => ({})); @@ -68,7 +68,7 @@ function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioCo cancelVariables: jest.fn(), templateVarsChangedInUrl: jest.fn(), dashboard: null, - theme: getTheme(), + theme: createTheme(), }; Object.assign(props, propOverrides); diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index 2e848352455..fa0e6c64ef5 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -5,7 +5,7 @@ import { hot } from 'react-hot-loader'; import { connect } from 'react-redux'; import { locationService } from '@grafana/runtime'; 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 { 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 { getTimeSrv } from '../services/TimeSrv'; 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 { DashboardFailed } from '../components/DashboardLoading/DashboardFailed'; @@ -50,7 +50,7 @@ type DashboardPageRouteSearchParams = { }; export interface Props - extends Themeable, + extends Themeable2, GrafanaRouteComponentProps { initPhase: DashboardInitPhase; isInitSlow: boolean; @@ -373,7 +373,7 @@ const mapDispatchToProps = { /* * Styles */ -export const getStyles = stylesFactory((theme: GrafanaTheme) => { +export const getStyles = stylesFactory((theme: GrafanaThemeV2) => { return { dashboardContainer: css` position: absolute; @@ -392,13 +392,13 @@ export const getStyles = stylesFactory((theme: GrafanaTheme) => { display: flex; `, dashboardContent: css` - padding: ${theme.spacing.md}; + padding: ${theme.spacing(2)}; flex-basis: 100%; flex-grow: 1; `, }; }); -export const DashboardPage = withTheme(UnthemedDashboardPage); +export const DashboardPage = withTheme2(UnthemedDashboardPage); DashboardPage.displayName = 'DashboardPage'; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage)); diff --git a/public/app/features/manage-dashboards/DashboardImportPage.tsx b/public/app/features/manage-dashboards/DashboardImportPage.tsx index 54f89dd896a..f61222fad44 100644 --- a/public/app/features/manage-dashboards/DashboardImportPage.tsx +++ b/public/app/features/manage-dashboards/DashboardImportPage.tsx @@ -1,8 +1,19 @@ import React, { FormEvent, PureComponent } from 'react'; import { MapDispatchToProps, MapStateToProps } from 'react-redux'; import { css } from '@emotion/css'; -import { AppEvents, NavModel } from '@grafana/data'; -import { Button, stylesFactory, Input, TextArea, Field, Form, Legend, FileUpload } from '@grafana/ui'; +import { AppEvents, GrafanaThemeV2, NavModel } from '@grafana/data'; +import { + Button, + stylesFactory, + withTheme2, + Input, + TextArea, + Field, + Form, + Legend, + FileUpload, + Themeable2, +} from '@grafana/ui'; import Page from 'app/core/components/Page/Page'; import { connectWithCleanUp } from 'app/core/components/connectWithCleanUp'; import { ImportDashboardOverview } from './components/ImportDashboardOverview'; @@ -12,7 +23,7 @@ import appEvents from 'app/core/app_events'; import { getNavModel } from 'app/core/selectors/navModel'; import { StoreState } from 'app/types'; -interface OwnProps {} +interface OwnProps extends Themeable2 {} interface ConnectedProps { navModel: NavModel; @@ -26,7 +37,7 @@ interface DispatchProps { type Props = OwnProps & ConnectedProps & DispatchProps; -class DashboardImportUnConnected extends PureComponent { +class UnthemedDashboardImport extends PureComponent { onFileUpload = (event: FormEvent) => { const { importDashboardJson } = this.props; const file = event.currentTarget.files && event.currentTarget.files.length > 0 && event.currentTarget.files[0]; @@ -72,7 +83,7 @@ class DashboardImportUnConnected extends PureComponent { }; renderImportForm() { - const styles = importStyles(); + const styles = importStyles(this.props.theme); return ( <> @@ -134,6 +145,8 @@ class DashboardImportUnConnected extends PureComponent { } } +const DashboardImportUnConnected = withTheme2(UnthemedDashboardImport); + const mapStateToProps: MapStateToProps = (state: StoreState) => ({ navModel: getNavModel(state.navIndex, 'import', undefined, true), isLoaded: state.importDashboard.isLoaded, @@ -154,10 +167,10 @@ export default DashboardImportPage; DashboardImportPage.displayName = 'DashboardImport'; -const importStyles = stylesFactory(() => { +const importStyles = stylesFactory((theme: GrafanaThemeV2) => { return { option: css` - margin-bottom: 32px; + margin-bottom: ${theme.spacing(4)}; `, }; }); diff --git a/public/app/features/playlist/PlaylistEditPage.tsx b/public/app/features/playlist/PlaylistEditPage.tsx index ed9dbb6377a..2643a95cee7 100644 --- a/public/app/features/playlist/PlaylistEditPage.tsx +++ b/public/app/features/playlist/PlaylistEditPage.tsx @@ -2,7 +2,7 @@ import React, { FC } from 'react'; import { connect, MapStateToProps } from 'react-redux'; import { NavModel } from '@grafana/data'; import { locationService } from '@grafana/runtime'; -import { useStyles } from '@grafana/ui'; +import { useStyles2 } from '@grafana/ui'; import Page from 'app/core/components/Page/Page'; import { StoreState } from 'app/types'; @@ -25,7 +25,7 @@ export interface RouteParams { interface Props extends ConnectedProps, GrafanaRouteComponentProps {} export const PlaylistEditPage: FC = ({ navModel, match }) => { - const styles = useStyles(getPlaylistStyles); + const styles = useStyles2(getPlaylistStyles); const { playlist, loading } = usePlaylist(match.params.id); const onSubmit = async (playlist: Playlist) => { await updatePlaylist(match.params.id, playlist); diff --git a/public/app/features/playlist/PlaylistNewPage.tsx b/public/app/features/playlist/PlaylistNewPage.tsx index dbb3dca1bcf..29449d91044 100644 --- a/public/app/features/playlist/PlaylistNewPage.tsx +++ b/public/app/features/playlist/PlaylistNewPage.tsx @@ -2,7 +2,7 @@ import React, { FC } from 'react'; import { connect, MapStateToProps } from 'react-redux'; import { NavModel } from '@grafana/data'; import { locationService } from '@grafana/runtime'; -import { useStyles } from '@grafana/ui'; +import { useStyles2 } from '@grafana/ui'; import Page from 'app/core/components/Page/Page'; import { StoreState } from 'app/types'; @@ -21,7 +21,7 @@ interface ConnectedProps { interface Props extends ConnectedProps, GrafanaRouteComponentProps {} export const PlaylistNewPage: FC = ({ navModel }) => { - const styles = useStyles(getPlaylistStyles); + const styles = useStyles2(getPlaylistStyles); const { playlist, loading } = usePlaylist(); const onSubmit = async (playlist: Playlist) => { await createPlaylist(playlist); diff --git a/public/app/features/playlist/styles.ts b/public/app/features/playlist/styles.ts index 43836c9e807..0cacc6505cd 100644 --- a/public/app/features/playlist/styles.ts +++ b/public/app/features/playlist/styles.ts @@ -1,7 +1,7 @@ -import { GrafanaTheme } from '@grafana/data'; +import { GrafanaThemeV2 } from '@grafana/data'; import { css } from '@emotion/css'; -export function getPlaylistStyles(theme: GrafanaTheme) { +export function getPlaylistStyles(theme: GrafanaThemeV2) { return { description: css` label: description; @@ -10,7 +10,7 @@ export function getPlaylistStyles(theme: GrafanaTheme) { `, subHeading: css` label: sub-heading; - margin-bottom: ${theme.spacing.md}; + margin-bottom: ${theme.spacing(2)}; `, }; } diff --git a/public/app/features/teams/CreateTeam.tsx b/public/app/features/teams/CreateTeam.tsx index 513df160746..8ef6c7ed77f 100644 --- a/public/app/features/teams/CreateTeam.tsx +++ b/public/app/features/teams/CreateTeam.tsx @@ -1,8 +1,7 @@ import React, { PureComponent } from 'react'; import Page from 'app/core/components/Page/Page'; import { hot } from 'react-hot-loader'; -import { Button, LegacyForms } from '@grafana/ui'; -const { FormField } = LegacyForms; +import { Button, Form, Field, Input, FieldSet, Label, Tooltip, Icon } from '@grafana/ui'; import { NavModel } from '@grafana/data'; import { getBackendSrv, locationService } from '@grafana/runtime'; import { connect } from 'react-redux'; @@ -13,78 +12,50 @@ export interface Props { navModel: NavModel; } -interface State { +interface TeamDTO { name: string; email: string; } -export class CreateTeam extends PureComponent { - state: State = { - name: '', - email: '', - }; - - create = async (event: React.FormEvent) => { - event.preventDefault(); - - const { name, email } = this.state; - - const result = await getBackendSrv().post('/api/teams', { name, email }); +export class CreateTeam extends PureComponent { + create = async (formModel: TeamDTO) => { + const result = await getBackendSrv().post('/api/teams', formModel); if (result.teamId) { locationService.push(`/org/teams/edit/${result.teamId}`); } }; - - onEmailChange = (event: React.ChangeEvent) => { - this.setState({ - email: event.target.value, - }); - }; - - onNameChange = (event: React.ChangeEvent) => { - this.setState({ - name: event.target.value, - }); - }; - render() { const { navModel } = this.props; - const { name, email } = this.state; return ( - <> -

New Team

- -
- - -
- -
- - +
+ {({ register }) => ( +
+ + + + + Email + + + + + } + > + + +
+ +
+
+ )} +
); diff --git a/public/app/features/teams/__snapshots__/CreateTeam.test.tsx.snap b/public/app/features/teams/__snapshots__/CreateTeam.test.tsx.snap index c06e4f817c8..a3f5e43773f 100644 --- a/public/app/features/teams/__snapshots__/CreateTeam.test.tsx.snap +++ b/public/app/features/teams/__snapshots__/CreateTeam.test.tsx.snap @@ -5,46 +5,11 @@ exports[`Render should render component 1`] = ` navModel={Object {}} > -

- New Team -

-
- - -
- -
- + +
`;