Theme: Page styles move to emotion global styles and design tweaks (#33529)

* Theme: Page styles move to emotion global styles and design tweaks

* More style tweaks

* tweaks

* Updating snapshots

* Another fix

* Another fix

* minor fix

* More style tweaks to page toolbar and alert rule page

* minor polish
This commit is contained in:
Torkel Ödegaard 2021-04-30 10:04:01 +02:00 committed by GitHub
parent aaca022df6
commit f6ecded86b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 367 additions and 433 deletions

View File

@ -135,7 +135,7 @@ const getStyles = (theme: GrafanaThemeV2) => {
background: ${theme.colors.background.canvas};
justify-content: flex-end;
flex-wrap: wrap;
padding: ${theme.spacing(0, 1, 1, 2)};
padding: ${theme.spacing(1, 2, 0, 2)};
`,
toolbarLeft: css`
display: flex;

View File

@ -4,10 +4,15 @@ import { useTheme2 } from '..';
import { getElementStyles } from './elements';
import { getCardStyles } from './card';
import { getAgularPanelStyles } from './angularPanelStyles';
import { getPageStyles } from './page';
/** @internal */
export function GlobalStyles() {
const theme = useTheme2();
return <Global styles={[getElementStyles(theme), getCardStyles(theme), getAgularPanelStyles(theme)]} />;
return (
<Global
styles={[getElementStyles(theme), getPageStyles(theme), getCardStyles(theme), getAgularPanelStyles(theme)]}
/>
);
}

View File

@ -0,0 +1,114 @@
import { css } from '@emotion/react';
import { GrafanaThemeV2 } from '@grafana/data';
export function getPageStyles(theme: GrafanaThemeV2) {
return css`
.grafana-app {
display: flex;
align-items: stretch;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.main-view {
position: relative;
flex-grow: 1;
}
.page-scrollbar-wrapper {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.page-scrollbar-content {
display: flex;
min-height: 100%;
flex-direction: column;
width: 100%;
height: 100%;
}
.page-container {
flex-grow: 1;
flex-basis: 100%;
padding-left: ${theme.spacing(2)};
padding-right: ${theme.spacing(2)};
${theme.breakpoints.up('sm')} {
margin: ${theme.spacing(0, 1)};
}
${theme.breakpoints.up('md')} {
margin: ${theme.spacing(0, 2)};
}
@media (min-width: ${theme.breakpoints.values.xxl + theme.spacing.gridSize * 2}px) {
max-width: ${theme.breakpoints.values.xxl}px;
margin-left: auto;
margin-right: auto;
width: 100%;
}
}
.page-full {
margin-left: ${theme.spacing(2)};
padding-left: ${theme.spacing(2)};
padding-right: ${theme.spacing(2)};
}
.page-body {
padding: ${theme.spacing(1)};
background: ${theme.components.panel.background};
border: 1px solid ${theme.components.panel.borderColor};
margin-bottom: 32px;
${theme.breakpoints.up('md')} {
padding: ${theme.spacing(2)};
}
${theme.breakpoints.up('lg')} {
padding: ${theme.spacing(3)};
}
}
.page-heading {
font-size: ${theme.typography.h4.fontSize};
margin-top: 0;
margin-bottom: ${theme.spacing(2)};
}
.page-action-bar {
margin-bottom: ${theme.spacing(2)};
display: flex;
align-items: flex-start;
> a,
> button {
margin-left: ${theme.spacing(2)};
}
}
.page-action-bar--narrow {
margin-bottom: 0;
}
.page-action-bar__spacer {
width: ${theme.spacing(2)};
flex-grow: 1;
}
.page-sub-heading {
margin-bottom: ${theme.spacing(2)};
}
.page-sub-heading-icon {
margin-left: ${theme.spacing(1)};
margin-top: ${theme.spacing(0.5)};
}
`;
}

View File

@ -372,7 +372,7 @@ $panel-editor-viz-item-shadow: 0 0 4px $gray-3;
$panel-editor-viz-item-border: 1px solid $gray-3;
$panel-editor-viz-item-shadow-hover: 0 0 4px $blue-light;
$panel-editor-viz-item-border-hover: 1px solid $blue-light;
$panel-editor-viz-item-bg: $white;
$panel-editor-viz-item-bg: $card-background;
$panel-editor-tabs-line-color: $dark-2;
$panel-editor-viz-item-bg-hover: lighten($blue-base, 45%);

View File

@ -23,7 +23,7 @@ export const FilterInput: FC<Props> = ({ value, placeholder, width, onChange, au
autoFocus={autoFocus ?? false}
prefix={<Icon name="search" />}
suffix={suffix}
width={width ?? 40}
width={width}
type="text"
value={value ? unEscapeStringFromRegex(value) : ''}
onChange={(event) => onChange(escapeStringForRegex(event.currentTarget.value))}

View File

@ -7,14 +7,13 @@ import PageHeader from '../PageHeader/PageHeader';
import { Footer } from '../Footer/Footer';
import { PageContents } from './PageContents';
import { CustomScrollbar, useStyles2 } from '@grafana/ui';
import { GrafanaThemeV2, NavModel, ThemeBreakpointsKey } from '@grafana/data';
import { GrafanaThemeV2, NavModel } from '@grafana/data';
import { Branding } from '../Branding/Branding';
import { css, cx } from '@emotion/css';
interface Props extends HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
navModel: NavModel;
contentWidth?: ThemeBreakpointsKey;
}
export interface PageType extends FC<Props> {
@ -22,7 +21,7 @@ export interface PageType extends FC<Props> {
Contents: typeof PageContents;
}
export const Page: PageType = ({ navModel, children, className, contentWidth, ...otherProps }) => {
export const Page: PageType = ({ navModel, children, className, ...otherProps }) => {
const styles = useStyles2(getStyles);
useEffect(() => {
@ -31,10 +30,7 @@ export const Page: PageType = ({ navModel, children, className, contentWidth, ..
}, [navModel]);
return (
<div
{...otherProps}
className={cx(styles.wrapper, className, contentWidth ? styles.contentWidth(contentWidth) : undefined)}
>
<div {...otherProps} className={cx(styles.wrapper, className)}>
<CustomScrollbar autoHeightMin={'100%'}>
<div className="page-scrollbar-content">
<PageHeader model={navModel} />
@ -53,15 +49,9 @@ export default Page;
const getStyles = (theme: GrafanaThemeV2) => ({
wrapper: css`
background: ${theme.colors.background.primary};
bottom: 0;
position: absolute;
top: 0;
width: 100%;
`,
contentWidth: (size: ThemeBreakpointsKey) => css`
.page-container {
max-width: ${theme.breakpoints.values[size]}px;
}
`,
});

View File

@ -24,7 +24,6 @@ export default class PageActionBar extends PureComponent<Props> {
<div className="gf-form gf-form--grow">
<FilterInput value={searchQuery} onChange={setSearchQuery} placeholder={placeholder} />
</div>
<div className="page-action-bar__spacer" />
{linkButton && <LinkButton {...linkProps}>{linkButton.title}</LinkButton>}
</div>
);

View File

@ -13,9 +13,6 @@ exports[`Render should render component 1`] = `
value=""
/>
</div>
<div
className="page-action-bar__spacer"
/>
<LinkButton
href="some/url"
target="_blank"

View File

@ -140,7 +140,6 @@ function renderTitle(title: string, breadcrumbs: NavModelBreadcrumb[]) {
const getStyles = (theme: GrafanaThemeV2) => ({
headerCanvas: css`
background: ${theme.colors.background.canvas};
border-bottom: 1px solid ${theme.colors.border.weak};
`,
});

View File

@ -3,13 +3,14 @@ import { css, cx } from '@emotion/css';
import { hot } from 'react-hot-loader';
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
import { NavModel } from '@grafana/data';
import { Pagination, Tooltip, HorizontalGroup, stylesFactory, LinkButton, Input, Icon } from '@grafana/ui';
import { Pagination, Tooltip, stylesFactory, LinkButton, Icon } from '@grafana/ui';
import { AccessControlAction, StoreState, UserDTO } from '../../types';
import Page from 'app/core/components/Page/Page';
import { getNavModel } from '../../core/selectors/navModel';
import { fetchUsers, changeQuery, changePage } from './state/actions';
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
import { contextSrv } from 'app/core/core';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
interface OwnProps {}
@ -42,27 +43,21 @@ const UserListAdminPageUnConnected: React.FC<Props> = (props) => {
<Page navModel={navModel}>
<Page.Contents>
<>
<div>
<HorizontalGroup justify="space-between">
<Input
width={40}
type="text"
<div className="page-action-bar">
<div className="gf-form gf-form--grow">
<FilterInput
placeholder="Search user by login, email, or name."
tabIndex={1}
autoFocus={true}
value={query}
spellCheck={false}
onChange={(event) => changeQuery(event.currentTarget.value)}
prefix={<Icon name="search" />}
onChange={(value) => changeQuery(value)}
/>
{contextSrv.hasPermission(AccessControlAction.UsersCreate) && (
<LinkButton href="admin/users/create" variant="primary">
New user
</LinkButton>
)}
</HorizontalGroup>
</div>
{contextSrv.hasPermission(AccessControlAction.UsersCreate) && (
<LinkButton href="admin/users/create" variant="primary">
New user
</LinkButton>
)}
</div>
<div className={cx(styles.table, 'admin-list-table')}>
<table className="filter-table form-inline filter-table--hover">
<thead>

View File

@ -16,7 +16,7 @@ export const AlertingPageWrapper: FC<Props> = ({ children, pageId, isLoading })
);
return (
<Page navModel={navModel} contentWidth="xxl">
<Page navModel={navModel}>
<Page.Contents isLoading={isLoading}>{children}</Page.Contents>
</Page>
);

View File

@ -1,6 +1,6 @@
import React, { FC, useMemo } from 'react';
import { GrafanaTheme } from '@grafana/data';
import { PageToolbar, ToolbarButton, useStyles, CustomScrollbar, Spinner, Alert } from '@grafana/ui';
import { GrafanaThemeV2 } from '@grafana/data';
import { PageToolbar, Button, useStyles2, CustomScrollbar, Spinner, Alert } from '@grafana/ui';
import { css } from '@emotion/css';
import { AlertTypeStep } from './AlertTypeStep';
@ -24,7 +24,7 @@ type Props = {
};
export const AlertRuleForm: FC<Props> = ({ existing }) => {
const styles = useStyles(getStyles);
const styles = useStyles2(getStyles);
const dispatch = useDispatch();
const defaultValues: RuleFormValues = useMemo(() => {
@ -73,22 +73,22 @@ export const AlertRuleForm: FC<Props> = ({ existing }) => {
return (
<FormProvider {...formAPI}>
<form onSubmit={handleSubmit((values) => submit(values, false))} className={styles.form}>
<PageToolbar title="Create alert rule" pageIcon="bell" className={styles.toolbar}>
<PageToolbar title="Create alert rule" pageIcon="bell">
<Link to="/alerting/list">
<ToolbarButton variant="default" disabled={submitState.loading} type="button">
<Button variant="secondary" disabled={submitState.loading} type="button" fill="outline">
Cancel
</ToolbarButton>
</Button>
</Link>
<ToolbarButton
variant="primary"
<Button
variant="secondary"
type="button"
onClick={handleSubmit((values) => submit(values, false))}
disabled={submitState.loading}
>
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
Save
</ToolbarButton>
<ToolbarButton
</Button>
<Button
variant="primary"
type="button"
onClick={handleSubmit((values) => submit(values, true))}
@ -96,7 +96,7 @@ export const AlertRuleForm: FC<Props> = ({ existing }) => {
>
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
Save and exit
</ToolbarButton>
</Button>
</PageToolbar>
<div className={styles.contentOuter}>
<CustomScrollbar autoHeightMin="100%" hideHorizontalTrack={true}>
@ -128,15 +128,10 @@ export const AlertRuleForm: FC<Props> = ({ existing }) => {
);
};
const getStyles = (theme: GrafanaTheme) => {
const getStyles = (theme: GrafanaThemeV2) => {
return {
buttonSpinner: css`
margin-right: ${theme.spacing.sm};
`,
toolbar: css`
padding-top: ${theme.spacing.sm};
padding-bottom: ${theme.spacing.md};
border-bottom: solid 1px ${theme.colors.border2};
margin-right: ${theme.spacing(1)};
`,
form: css`
width: 100%;
@ -146,19 +141,16 @@ const getStyles = (theme: GrafanaTheme) => {
`,
contentInner: css`
flex: 1;
padding: ${theme.spacing.md};
padding: ${theme.spacing(2)};
`,
contentOuter: css`
background: ${theme.colors.panelBg};
background: ${theme.colors.background.primary};
border: 1px solid ${theme.colors.border.weak};
border-radius: ${theme.shape.borderRadius()};
margin: ${theme.spacing(2)};
overflow: hidden;
flex: 1;
`,
formInput: css`
width: 400px;
& + & {
margin-left: ${theme.spacing.sm};
}
`,
flexRow: css`
display: flex;
flex-direction: row;

View File

@ -15,8 +15,6 @@ export const ApiKeysActionBar: FC<Props> = ({ searchQuery, disabled, onAddClick,
<div className="gf-form gf-form--grow">
<FilterInput placeholder="Search keys" value={searchQuery} onChange={onSearchChange} />
</div>
<div className="page-action-bar__spacer" />
<Button className="pull-right" onClick={onAddClick} disabled={disabled}>
Add API key
</Button>

View File

@ -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 { InlineField, InlineSwitch } from '@grafana/ui';
import { InlineField, InlineSwitch, VerticalGroup } from '@grafana/ui';
import { rangeUtil } from '@grafana/data';
import { getTimeZone } from 'app/features/profile/state/selectors';
import { setSearchQuery } from './state/reducers';
@ -150,13 +150,12 @@ export class ApiKeysPageUnconnected extends PureComponent<Props, State> {
) : null}
<ApiKeysForm show={isAdding} onClose={toggleIsAdding} onKeyAdded={this.onAddApiKey} />
{showTable ? (
<>
<h3 className="page-heading">Existing API keys</h3>
<VerticalGroup>
<InlineField label="Show expired">
<InlineSwitch id="showExpired" value={includeExpired} onChange={this.onIncludeExpiredChange} />
</InlineField>
<ApiKeysTable apiKeys={apiKeys} timeZone={timeZone} onDelete={this.onDeleteApiKey} />
</>
</VerticalGroup>
) : null}
</>
);

View File

@ -74,7 +74,7 @@ const getStyles = (theme: GrafanaTheme) => {
min-height: 0;
`,
vizButtonWrapper: css`
padding: 0 ${theme.spacing.sm} ${theme.spacing.md} 0;
padding: 0 ${theme.spacing.md} ${theme.spacing.md} 0;
`,
legacyOptions: css`
label: legacy-options;

View File

@ -40,7 +40,7 @@ export const SnapshotListTable: FC = () => {
);
return (
<div className="page-container page-body">
<div>
<table className="filter-table">
<thead>
<tr>

View File

@ -38,7 +38,7 @@ export class OrgDetailsPage extends PureComponent<Props> {
<Page navModel={navModel}>
<Page.Contents isLoading={isLoading}>
{!isLoading && (
<VerticalGroup>
<VerticalGroup spacing="lg">
<OrgProfile onSubmit={this.onUpdateOrganization} orgName={organization.name} />
<SharedPreferences resourceUri="org" />
</VerticalGroup>

View File

@ -35,7 +35,9 @@ exports[`Render should render organization and preferences 1`] = `
<PageContents
isLoading={false}
>
<VerticalGroup>
<VerticalGroup
spacing="lg"
>
<OrgProfile
onSubmit={[Function]}
orgName="Cool org"

View File

@ -17,7 +17,7 @@ import {
UrlQueryMap,
} from '@grafana/data';
import { AppNotificationSeverity } from 'app/types';
import { Alert, InfoBox, LinkButton, PluginSignatureBadge, Tooltip } from '@grafana/ui';
import { Alert, LinkButton, PluginSignatureBadge, Tooltip } from '@grafana/ui';
import Page from 'app/core/components/Page/Page';
import { getPluginSettings } from './PluginSettingsCache';
@ -170,9 +170,6 @@ class PluginPage extends PureComponent<Props, State> {
<LinkButton fill="text" onClick={this.showUpdateInfo}>
Update Available!
</LinkButton>
{/*<a href="#" onClick={this.showUpdateInfo}>*/}
{/* Update Available!*/}
{/*</a>*/}
</Tooltip>
</div>
)}
@ -283,11 +280,10 @@ class PluginPage extends PureComponent<Props, State> {
}
return (
<InfoBox
<Alert
aria-label={selectors.pages.PluginPage.signatureInfo}
severity={plugin.meta.signature !== PluginSignatureStatus.valid ? 'warning' : 'info'}
urlTitle="Read more about plugins signing"
url="https://grafana.com/docs/grafana/latest/plugins/plugin-signatures/"
title="Plugin signature"
>
<PluginSignatureBadge
status={plugin.meta.signature}
@ -303,7 +299,15 @@ class PluginPage extends PureComponent<Props, State> {
{plugin.meta.signature !== PluginSignatureStatus.valid &&
'Grafana Labs cant guarantee the integrity of this unsigned plugin. Ask the plugin author to request it to be signed.'}
</p>
</InfoBox>
<a
href="https://grafana.com/docs/grafana/latest/plugins/plugin-signatures/"
className="external-link"
target="_blank"
rel="noreferrer"
>
Read more about plugins signing.
</a>
</Alert>
);
}

View File

@ -62,7 +62,7 @@ export const ChangePasswordForm: FC<Props> = ({ user, onChangePassword, isSaving
<Button variant="primary" disabled={isSaving}>
Change Password
</Button>
<LinkButton variant="secondary" href={`${config.appSubUrl}/profile`}>
<LinkButton variant="secondary" href={`${config.appSubUrl}/profile`} fill="outline">
Cancel
</LinkButton>
</HorizontalGroup>

View File

@ -27,7 +27,7 @@ export const ChangePasswordPage: FC<Props> = ({ navModel }) => (
) => {
return (
<Page.Contents>
<h3 className="page-sub-heading">Change Your Password</h3>
<h3 className="page-heading">Change Your Password</h3>
{states.loadUser ? (
<LoadingPlaceholder text="Loading user profile..." />
) : (

View File

@ -22,52 +22,52 @@ export class UserOrganizations extends PureComponent<Props> {
return <LoadingPlaceholder text="Loading organizations..." />;
}
if (orgs.length === 0) {
return null;
}
return (
<>
{orgs.length > 0 && (
<>
<h3 className="page-sub-heading">Organizations</h3>
<div className="gf-form-group">
<table className="filter-table form-inline">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th />
<div>
<h3 className="page-sub-heading">Organizations</h3>
<div className="gf-form-group">
<table className="filter-table form-inline">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th />
</tr>
</thead>
<tbody>
{orgs.map((org: UserOrg, index) => {
return (
<tr key={index}>
<td>{org.name}</td>
<td>{org.role}</td>
<td className="text-right">
{org.orgId === user.orgId ? (
<Button variant="secondary" size="sm" disabled>
Current
</Button>
) : (
<Button
variant="secondary"
size="sm"
onClick={() => {
this.props.setUserOrg(org);
}}
>
Select
</Button>
)}
</td>
</tr>
</thead>
<tbody>
{orgs.map((org: UserOrg, index) => {
return (
<tr key={index}>
<td>{org.name}</td>
<td>{org.role}</td>
<td className="text-right">
{org.orgId === user.orgId ? (
<Button variant="secondary" size="sm" disabled>
Current
</Button>
) : (
<Button
variant="secondary"
size="sm"
onClick={() => {
this.props.setUserOrg(org);
}}
>
Select
</Button>
)}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</>
)}
</>
);
})}
</tbody>
</table>
</div>
</div>
);
}
}

View File

@ -1,7 +1,7 @@
import React, { FC } from 'react';
import { connect } from 'react-redux';
import { hot } from 'react-hot-loader';
import { LoadingPlaceholder } from '@grafana/ui';
import { LoadingPlaceholder, VerticalGroup } from '@grafana/ui';
import { config } from '@grafana/runtime';
import { NavModel } from '@grafana/data';
import { UserProvider, UserAPI, LoadingStates } from 'app/core/utils/UserProvider';
@ -34,16 +34,15 @@ export const UserProfileEdit: FC<Props> = ({ navModel }) => (
{states.loadUser ? (
<LoadingPlaceholder text="Loading user profile..." />
) : (
<UserProfileEditForm
updateProfile={api.updateUserProfile}
isSavingUser={states.updateUserProfile}
user={user!}
/>
)}
<SharedPreferences resourceUri="user" />
<UserTeams isLoading={states.loadTeams} loadTeams={api.loadTeams} teams={teams} />
{!states.loadUser && (
<>
<VerticalGroup spacing="md">
<UserProfileEditForm
updateProfile={api.updateUserProfile}
isSavingUser={states.updateUserProfile}
user={user!}
/>
<SharedPreferences resourceUri="user" />
<UserTeams isLoading={states.loadTeams} loadTeams={api.loadTeams} teams={teams} />
<UserOrganizations
isLoading={states.loadOrgs}
setUserOrg={api.setUserOrg}
@ -58,7 +57,7 @@ export const UserProfileEdit: FC<Props> = ({ navModel }) => (
sessions={sessions}
user={user!}
/>
</>
</VerticalGroup>
)}
</Page.Contents>
);

View File

@ -23,7 +23,7 @@ export class UserSessions extends PureComponent<Props> {
}
return (
<>
<div>
{sessions.length > 0 && (
<>
<h3 className="page-sub-heading">Sessions</h3>
@ -59,7 +59,7 @@ export class UserSessions extends PureComponent<Props> {
</div>
</>
)}
</>
</div>
);
}
}

View File

@ -20,40 +20,40 @@ export class UserTeams extends PureComponent<Props> {
return <LoadingPlaceholder text="Loading teams..." />;
}
if (teams.length === 0) {
return null;
}
return (
<>
{teams.length > 0 && (
<>
<h3 className="page-sub-heading">Teams</h3>
<div className="gf-form-group">
<table className="filter-table form-inline">
<thead>
<tr>
<th />
<th>Name</th>
<th>Email</th>
<th>Members</th>
<div>
<h3 className="page-sub-heading">Teams</h3>
<div className="gf-form-group">
<table className="filter-table form-inline">
<thead>
<tr>
<th />
<th>Name</th>
<th>Email</th>
<th>Members</th>
</tr>
</thead>
<tbody>
{teams.map((team: Team, index) => {
return (
<tr key={index}>
<td className="width-4 text-center">
<img className="filter-table__avatar" src={team.avatarUrl} />
</td>
<td>{team.name}</td>
<td>{team.email}</td>
<td>{team.memberCount}</td>
</tr>
</thead>
<tbody>
{teams.map((team: Team, index) => {
return (
<tr key={index}>
<td className="width-4 text-center">
<img className="filter-table__avatar" src={team.avatarUrl} />
</td>
<td>{team.name}</td>
<td>{team.email}</td>
<td>{team.memberCount}</td>
</tr>
);
})}
</tbody>
</table>
</div>
</>
)}
</>
);
})}
</tbody>
</table>
</div>
</div>
);
}
}

View File

@ -19,10 +19,12 @@ export const DashboardActions: FC<Props> = ({ folderId, isEditor, canEdit }) =>
};
return (
<HorizontalGroup spacing="md" align="center">
{canEdit && <LinkButton href={actionUrl('new')}>New Dashboard</LinkButton>}
{!folderId && isEditor && <LinkButton href="dashboards/folder/new">New Folder</LinkButton>}
{canEdit && <LinkButton href={actionUrl('import')}>Import</LinkButton>}
</HorizontalGroup>
<div>
<HorizontalGroup spacing="md" align="center">
{canEdit && <LinkButton href={actionUrl('new')}>New Dashboard</LinkButton>}
{!folderId && isEditor && <LinkButton href="dashboards/folder/new">New Folder</LinkButton>}
{canEdit && <LinkButton href={actionUrl('import')}>Import</LinkButton>}
</HorizontalGroup>
</div>
);
};

View File

@ -1,6 +1,6 @@
import React, { FC, memo, useState } from 'react';
import { css } from '@emotion/css';
import { HorizontalGroup, stylesFactory, useTheme, Spinner } from '@grafana/ui';
import { stylesFactory, useTheme, Spinner } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
import { contextSrv } from 'app/core/services/context_srv';
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
@ -93,11 +93,11 @@ export const ManageDashboards: FC<Props> = memo(({ folder }) => {
return (
<div className={styles.container}>
<div>
<HorizontalGroup justify="space-between">
<div className="page-action-bar">
<div className="gf-form gf-form--grow m-r-2">
<FilterInput value={query.query} onChange={onQueryChange} placeholder={'Search dashboards by name'} />
<DashboardActions isEditor={isEditor} canEdit={hasEditPermissionInFolders || canSave} folderId={folderId} />
</HorizontalGroup>
</div>
<DashboardActions isEditor={isEditor} canEdit={hasEditPermissionInFolders || canSave} folderId={folderId} />
</div>
<div className={styles.results}>

View File

@ -101,8 +101,6 @@ export class TeamList extends PureComponent<Props, any> {
<FilterInput placeholder="Search teams" value={searchQuery} onChange={this.onSearchQueryChange} />
</div>
<div className="page-action-bar__spacer" />
<LinkButton className={disabledClass} href={newTeamHref}>
New Team
</LinkButton>

View File

@ -78,8 +78,6 @@ export class TeamMembers extends PureComponent<Props, State> {
<div className="gf-form gf-form--grow">
<FilterInput placeholder="Search members" value={searchMemberQuery} onChange={this.onSearchQueryChange} />
</div>
<div className="page-action-bar__spacer" />
<Button className="pull-right" onClick={this.onToggleAdding} disabled={isAdding || !isTeamAdmin}>
Add member
</Button>

View File

@ -47,9 +47,6 @@ exports[`Render should render teams table 1`] = `
value=""
/>
</div>
<div
className="page-action-bar__spacer"
/>
<LinkButton
className=""
href="org/teams/new"
@ -380,9 +377,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned on and signedin us
value=""
/>
</div>
<div
className="page-action-bar__spacer"
/>
<LinkButton
className=" disabled"
href="#"
@ -505,9 +499,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned on and signedin us
value=""
/>
</div>
<div
className="page-action-bar__spacer"
/>
<LinkButton
className=""
href="org/teams/new"

View File

@ -14,9 +14,6 @@ exports[`Render should render component 1`] = `
value=""
/>
</div>
<div
className="page-action-bar__spacer"
/>
<Button
className="pull-right"
disabled={false}
@ -101,9 +98,6 @@ exports[`Render should render team members 1`] = `
value=""
/>
</div>
<div
className="page-action-bar__spacer"
/>
<Button
className="pull-right"
disabled={false}

View File

@ -44,19 +44,18 @@ export class UsersActionBar extends PureComponent<Props> {
onChange={setUsersSearchQuery}
placeholder="Search user by login, email or name"
/>
{pendingInvitesCount > 0 && (
<div style={{ marginLeft: '1rem' }}>
<RadioButtonGroup value={showInvites ? 'invites' : 'users'} options={options} onChange={onShowInvites} />
</div>
)}
<div className="page-action-bar__spacer" />
{canInvite && canAddToOrg && <LinkButton href="org/users/invite">Invite</LinkButton>}
{externalUserMngLinkUrl && (
<LinkButton href={externalUserMngLinkUrl} target="_blank" rel="noopener">
{externalUserMngLinkName}
</LinkButton>
)}
</div>
{pendingInvitesCount > 0 && (
<div style={{ marginLeft: '1rem' }}>
<RadioButtonGroup value={showInvites ? 'invites' : 'users'} options={options} onChange={onShowInvites} />
</div>
)}
{canInvite && canAddToOrg && <LinkButton href="org/users/invite">Invite</LinkButton>}
{externalUserMngLinkUrl && (
<LinkButton href={externalUserMngLinkUrl} target="_blank" rel="noopener">
{externalUserMngLinkName}
</LinkButton>
)}
</div>
);
}

View File

@ -12,9 +12,6 @@ exports[`Render should render component 1`] = `
placeholder="Search user by login, email or name"
value=""
/>
<div
className="page-action-bar__spacer"
/>
</div>
</div>
`;
@ -31,32 +28,29 @@ exports[`Render should render pending invites button 1`] = `
placeholder="Search user by login, email or name"
value=""
/>
<div
style={
Object {
"marginLeft": "1rem",
}
</div>
<div
style={
Object {
"marginLeft": "1rem",
}
>
<RadioButtonGroup
onChange={[MockFunction]}
options={
Array [
Object {
"label": "Users",
"value": "users",
},
Object {
"label": "Pending Invites (5)",
"value": "invites",
},
]
}
value="users"
/>
</div>
<div
className="page-action-bar__spacer"
}
>
<RadioButtonGroup
onChange={[MockFunction]}
options={
Array [
Object {
"label": "Users",
"value": "users",
},
Object {
"label": "Pending Invites (5)",
"value": "invites",
},
]
}
value="users"
/>
</div>
</div>
@ -74,15 +68,12 @@ exports[`Render should show external user management button 1`] = `
placeholder="Search user by login, email or name"
value=""
/>
<div
className="page-action-bar__spacer"
/>
<LinkButton
href="some/url"
rel="noopener"
target="_blank"
/>
</div>
<LinkButton
href="some/url"
rel="noopener"
target="_blank"
/>
</div>
`;
@ -98,14 +89,11 @@ exports[`Render should show invite button 1`] = `
placeholder="Search user by login, email or name"
value=""
/>
<div
className="page-action-bar__spacer"
/>
<LinkButton
href="org/users/invite"
>
Invite
</LinkButton>
</div>
<LinkButton
href="org/users/invite"
>
Invite
</LinkButton>
</div>
`;

View File

@ -29,7 +29,6 @@
// LAYOUTS
@import 'layout/lists';
@import 'layout/page';
// COMPONENTS
@import '../app/features/dashboard/components/AddPanelWidget/AddPanelWidget';

View File

@ -374,7 +374,7 @@ $panel-editor-viz-item-shadow: 0 0 4px $gray-3;
$panel-editor-viz-item-border: 1px solid $gray-3;
$panel-editor-viz-item-shadow-hover: 0 0 4px $blue-light;
$panel-editor-viz-item-border-hover: 1px solid $blue-light;
$panel-editor-viz-item-bg: $white;
$panel-editor-viz-item-bg: $card-background;
$panel-editor-tabs-line-color: $dark-2;
$panel-editor-viz-item-bg-hover: lighten($blue-base, 45%);

View File

@ -32,6 +32,15 @@ mark,
background: $alert-warning-bg;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 500;
}
// Lists
// --------------------------------------------------

View File

@ -1,6 +1,4 @@
.page-header {
padding: $space-xl 0 0 0;
.btn {
float: right;
margin-left: $space-md;
@ -11,12 +9,18 @@
top: 1px;
}
}
margin-top: $space-md;
@include media-breakpoint-up(md) {
margin-top: 0;
}
}
.page-header__inner {
flex-grow: 1;
display: flex;
margin-bottom: $space-xl;
padding: $space-lg 0;
}
.page-header__title {
@ -36,6 +40,7 @@
width: 50px;
height: 50px;
position: relative;
color: $text-color-weak;
&.fa {
top: 10px;
@ -51,7 +56,8 @@
}
.page-header__logo {
margin: -1px $spacer;
margin: -1px $spacer -1px 0;
color: $text-color-weak;
}
.page-header__sub-title {

View File

@ -23,7 +23,6 @@ $mobile-menu-breakpoint: md;
@include media-breakpoint-up($mobile-menu-breakpoint) {
background: $side-menu-bg;
height: auto;
box-shadow: $side-menu-shadow;
position: relative;
z-index: $zindex-sidemenu;
}

View File

@ -1,165 +0,0 @@
.grafana-app {
display: flex;
align-items: stretch;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.main-view {
position: relative;
flex-grow: 1;
background: $page-bg;
}
.page-alerting,
.page-explore,
.page-dashboard {
.main-view {
background: $dashboard-bg;
}
}
.page-scrollbar-wrapper {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.page-scrollbar-content {
display: flex;
min-height: 100%;
flex-direction: column;
width: 100%;
height: 100%;
}
.page-container {
flex-grow: 1;
width: 100%;
flex-basis: 100%;
margin-left: auto;
margin-right: auto;
padding-left: $spacer * 2;
padding-right: $spacer * 2;
max-width: 980px;
@include clearfix();
}
.page-full {
margin-left: $page-sidebar-margin;
padding-left: $spacer;
padding-right: $spacer;
@include clearfix();
}
.scroll-canvas {
position: absolute;
width: 100%;
overflow: auto;
height: 100%;
-webkit-overflow-scrolling: touch;
display: flex;
flex-direction: column;
&--dashboard {
height: calc(100% - 56px);
}
> div {
flex-grow: 1;
}
> .footer {
flex-shrink: 0;
}
// Render in correct position even ng-view div is not rendered yet
> .footer:first-child {
flex-grow: 1;
display: flex;
> * {
width: 100%;
align-self: flex-end;
}
}
}
.page-body {
padding-top: $spacer * 2;
padding-bottom: $spacer * 4;
}
.page-heading {
font-size: $font-size-h4;
margin-top: 0;
margin-bottom: $spacer;
}
.page-action-bar {
margin-bottom: $spacer * 2;
display: flex;
align-items: flex-start;
> a,
> button {
margin-left: $spacer;
}
}
.page-action-bar--narrow {
margin-bottom: 0;
}
.page-action-bar__spacer {
width: $spacer * 2;
flex-grow: 1;
}
.sidebar-content {
width: calc(100% - #{$page-sidebar-width + $page-sidebar-margin}); // sidebar width + margin
}
.sidebar-container {
@include media-breakpoint-up(md) {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
}
.page-sidebar {
@include media-breakpoint-up(md) {
width: $page-sidebar-width;
margin-left: $page-sidebar-margin;
}
}
.page-sub-heading {
margin-bottom: $spacer;
}
.page-sub-heading-icon {
margin-left: $spacer;
margin-top: $space-xs;
}
.page-sidebar {
color: $text-color-weak;
h4 {
font-size: $font-size-base;
font-weight: $font-weight-semi-bold;
}
h5 {
font-size: $font-size-base;
font-weight: $font-weight-semi-bold;
}
}
.page-sidebar-section {
margin-bottom: $spacer * 2;
}

View File

@ -1,3 +1,26 @@
.sidebar-content {
width: calc(100% - #{$page-sidebar-width + $page-sidebar-margin}); // sidebar width + margin
}
.sidebar-container {
@include media-breakpoint-up(md) {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
}
.page-sidebar {
@include media-breakpoint-up(md) {
width: $page-sidebar-width;
margin-left: $page-sidebar-margin;
}
}
.page-sidebar-section {
margin-bottom: $spacer * 2;
}
.get-more-plugins-link {
color: $gray-3;
font-size: $font-size-sm;