mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Modals: design update (#33368)
* Modals: Style update draft * Modal.ButtonRow to control spacing better in a centralised way * Remove header border if no tabs * Added border and made buttons right aligned and changed order of buttons * Updating the overlay * Tweaks to paddings * Updated share modals
This commit is contained in:
parent
de65cef850
commit
4643bfa539
@ -74,7 +74,7 @@ export function createComponents(colors: ThemeColors, shadows: ThemeShadows): Th
|
||||
padding: 1,
|
||||
},
|
||||
overlay: {
|
||||
background: colors.mode === 'dark' ? 'rgba(0, 0, 0, 0.27)' : 'rgba(208, 209, 211, 0.24)',
|
||||
background: colors.mode === 'dark' ? 'rgba(0, 0, 0, 0.45)' : 'rgba(208, 209, 211, 0.24)',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -57,37 +57,35 @@ export const ConfirmModal = ({
|
||||
|
||||
return (
|
||||
<Modal className={styles.modal} title={title} icon={icon} isOpen={isOpen} onDismiss={onDismiss}>
|
||||
<div className={styles.modalContent}>
|
||||
<div className={styles.modalText}>
|
||||
{body}
|
||||
{description ? <div className={styles.modalDescription}>{description}</div> : null}
|
||||
{confirmationText ? (
|
||||
<div className={styles.modalConfirmationInput}>
|
||||
<HorizontalGroup justify="center">
|
||||
<Input placeholder={`Type ${confirmationText} to confirm`} onChange={onConfirmationTextChange} />
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<HorizontalGroup justify="center">
|
||||
{onAlternative ? (
|
||||
<Button variant="primary" onClick={onAlternative}>
|
||||
{alternativeText}
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={onConfirm}
|
||||
disabled={disabled}
|
||||
aria-label={selectors.pages.ConfirmModal.delete}
|
||||
>
|
||||
{confirmText}
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
{dismissText}
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
<div className={styles.modalText}>
|
||||
{body}
|
||||
{description ? <div className={styles.modalDescription}>{description}</div> : null}
|
||||
{confirmationText ? (
|
||||
<div className={styles.modalConfirmationInput}>
|
||||
<HorizontalGroup>
|
||||
<Input placeholder={`Type ${confirmationText} to confirm`} onChange={onConfirmationTextChange} />
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
{dismissText}
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={onConfirm}
|
||||
disabled={disabled}
|
||||
aria-label={selectors.pages.ConfirmModal.delete}
|
||||
>
|
||||
{confirmText}
|
||||
</Button>
|
||||
{onAlternative ? (
|
||||
<Button variant="primary" onClick={onAlternative}>
|
||||
{alternativeText}
|
||||
</Button>
|
||||
) : null}
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@ -96,20 +94,14 @@ const getStyles = (theme: GrafanaThemeV2) => ({
|
||||
modal: css`
|
||||
width: 500px;
|
||||
`,
|
||||
modalContent: css`
|
||||
text-align: center;
|
||||
`,
|
||||
modalText: css({
|
||||
fontSize: theme.typography.h4.fontSize,
|
||||
fontSize: theme.typography.h5.fontSize,
|
||||
color: theme.colors.text.primary,
|
||||
marginBottom: `calc(${theme.spacing(2)}*2)`,
|
||||
paddingTop: theme.spacing(2),
|
||||
}),
|
||||
modalDescription: css({
|
||||
fontSize: theme.typography.h6.fontSize,
|
||||
paddingTop: theme.spacing(2),
|
||||
fontSize: theme.typography.body.fontSize,
|
||||
}),
|
||||
modalConfirmationInput: css({
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingTop: theme.spacing(1),
|
||||
}),
|
||||
});
|
||||
|
@ -26,6 +26,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
wrapper: css`
|
||||
margin-bottom: ${theme.spacing.formSpacingBase * 4}px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
@ -54,7 +54,7 @@ IconButton.displayName = 'IconButton';
|
||||
const getStyles = stylesFactory((theme: GrafanaThemeV2, size: IconSize) => {
|
||||
const hoverColor = theme.colors.action.hover;
|
||||
const pixelSize = getSvgSize(size);
|
||||
const hoverSize = pixelSize / 2;
|
||||
const hoverSize = Math.max(pixelSize / 3, 8);
|
||||
|
||||
return {
|
||||
button: css`
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { oneLineTrim } from 'common-tags';
|
||||
import { Story, Meta } from '@storybook/react';
|
||||
import { getAvailableIcons } from '../../types';
|
||||
import { Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
|
||||
import { Button, Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
|
||||
import { css, cx } from '@emotion/css';
|
||||
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
@ -54,6 +54,10 @@ export const Basic: Story = ({ body, title, ...args }) => {
|
||||
return (
|
||||
<Modal title={title} {...args}>
|
||||
{body}
|
||||
<Modal.ButtonRow>
|
||||
<Button>Button1</Button>
|
||||
<Button variant="secondary">Cancel</Button>
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { FC, PropsWithChildren, useCallback, useEffect } from 'react';
|
||||
import React, { PropsWithChildren, useCallback, useEffect } from 'react';
|
||||
import { Portal } from '../Portal/Portal';
|
||||
import { cx } from '@emotion/css';
|
||||
import { useTheme2 } from '../../themes';
|
||||
@ -6,9 +6,12 @@ import { IconName } from '../../types';
|
||||
import { getModalStyles } from './getModalStyles';
|
||||
import { ModalHeader } from './ModalHeader';
|
||||
import { IconButton } from '../IconButton/IconButton';
|
||||
import { HorizontalGroup } from '../Layout/Layout';
|
||||
|
||||
export interface Props {
|
||||
/** @deprecated no longer used */
|
||||
icon?: IconName;
|
||||
/** @deprecated no longer used */
|
||||
iconTooltip?: string;
|
||||
/** Title for the modal or custom header element */
|
||||
title: string | JSX.Element;
|
||||
@ -23,7 +26,7 @@ export interface Props {
|
||||
onClickBackdrop?: () => void;
|
||||
}
|
||||
|
||||
export function Modal(props: PropsWithChildren<Props>): ReturnType<FC<Props>> {
|
||||
export function Modal(props: PropsWithChildren<Props>) {
|
||||
const {
|
||||
title,
|
||||
children,
|
||||
@ -62,14 +65,16 @@ export function Modal(props: PropsWithChildren<Props>): ReturnType<FC<Props>> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const headerClass = cx(styles.modalHeader, typeof title !== 'string' && styles.modalHeaderWithTabs);
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<div className={cx(styles.modal, className)}>
|
||||
<div className={styles.modalHeader}>
|
||||
<div className={headerClass}>
|
||||
{typeof title === 'string' && <DefaultModalHeader {...props} title={title} />}
|
||||
{typeof title !== 'string' && title}
|
||||
<div className={styles.modalHeaderClose}>
|
||||
<IconButton surface="header" name="times" size="lg" onClick={onDismiss} />
|
||||
<IconButton surface="header" name="times" size="xl" onClick={onDismiss} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.modalContent, contentClassName)}>{children}</div>
|
||||
@ -79,6 +84,21 @@ export function Modal(props: PropsWithChildren<Props>): ReturnType<FC<Props>> {
|
||||
);
|
||||
}
|
||||
|
||||
function ModalButtonRow({ children }: { children: React.ReactNode }) {
|
||||
const theme = useTheme2();
|
||||
const styles = getModalStyles(theme);
|
||||
|
||||
return (
|
||||
<div className={styles.modalButtonRow}>
|
||||
<HorizontalGroup justify="flex-end" spacing="md">
|
||||
{children}
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Modal.ButtonRow = ModalButtonRow;
|
||||
|
||||
interface DefaultModalHeaderProps {
|
||||
title: string;
|
||||
icon?: IconName;
|
||||
|
@ -2,29 +2,22 @@ import React from 'react';
|
||||
import { getModalStyles } from './getModalStyles';
|
||||
import { IconName } from '../../types';
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { Tooltip } from '..';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
/** @deprecated */
|
||||
icon?: IconName;
|
||||
/** @deprecated */
|
||||
iconTooltip?: string;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export const ModalHeader: React.FC<Props> = ({ icon, iconTooltip, title, children }) => {
|
||||
const styles = useStyles2(getModalStyles);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className={styles.modalHeaderTitle}>
|
||||
{icon && !iconTooltip && <Icon name={icon} size="lg" className={styles.modalHeaderIcon} />}
|
||||
{icon && iconTooltip && (
|
||||
<Tooltip content={iconTooltip}>
|
||||
<Icon name={icon} size="lg" className={styles.modalHeaderIcon} />
|
||||
</Tooltip>
|
||||
)}
|
||||
{title}
|
||||
</h2>
|
||||
<h2 className={styles.modalHeaderTitle}>{title}</h2>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
|
@ -3,7 +3,7 @@ import { GrafanaThemeV2 } from '@grafana/data';
|
||||
import { stylesFactory } from '../../themes';
|
||||
|
||||
export const getModalStyles = stylesFactory((theme: GrafanaThemeV2) => {
|
||||
const borderRadius = theme.shape.borderRadius(2);
|
||||
const borderRadius = theme.shape.borderRadius(1);
|
||||
|
||||
return {
|
||||
modal: css`
|
||||
@ -12,6 +12,7 @@ export const getModalStyles = stylesFactory((theme: GrafanaThemeV2) => {
|
||||
background: ${theme.colors.background.primary};
|
||||
box-shadow: ${theme.shadows.z3};
|
||||
border-radius: ${borderRadius};
|
||||
border: 1px solid ${theme.colors.border.weak};
|
||||
background-clip: padding-box;
|
||||
outline: none;
|
||||
width: 750px;
|
||||
@ -34,17 +35,21 @@ export const getModalStyles = stylesFactory((theme: GrafanaThemeV2) => {
|
||||
`,
|
||||
modalHeader: css`
|
||||
label: modalHeader;
|
||||
background: ${theme.colors.background.secondary};
|
||||
border-radius: ${borderRadius} ${borderRadius} 0 0;
|
||||
display: flex;
|
||||
height: 42px;
|
||||
align-items: center;
|
||||
min-height: 42px;
|
||||
margin: ${theme.spacing(1, 2, 0, 2)};
|
||||
`,
|
||||
modalHeaderWithTabs: css`
|
||||
border-bottom: 1px solid ${theme.colors.border.weak};
|
||||
`,
|
||||
modalHeaderTitle: css`
|
||||
font-size: ${theme.typography.size.lg};
|
||||
margin: 0 ${theme.spacing(2)};
|
||||
margin: ${theme.spacing(0, 4, 0, 1)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 42px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
`,
|
||||
modalHeaderIcon: css`
|
||||
margin-right: ${theme.spacing(2)};
|
||||
@ -57,15 +62,18 @@ export const getModalStyles = stylesFactory((theme: GrafanaThemeV2) => {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: ${theme.colors.text.secondary};
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
padding-right: ${theme.spacing(1)};
|
||||
`,
|
||||
modalContent: css`
|
||||
padding: ${theme.spacing(2)};
|
||||
padding: ${theme.spacing(3)};
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
max-height: calc(90vh - ${theme.spacing(2)});
|
||||
max-height: calc(90vh - ${theme.spacing(4)});
|
||||
`,
|
||||
modalButtonRow: css`
|
||||
padding-top: ${theme.spacing(3)};
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
@ -10,7 +10,6 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
|
||||
const getTabContentStyle = stylesFactory((theme: GrafanaThemeV2) => {
|
||||
return {
|
||||
tabContent: css`
|
||||
padding: ${theme.spacing(1)};
|
||||
background: ${theme.colors.background.primary};
|
||||
`,
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { sumBy } from 'lodash';
|
||||
import { Modal, ConfirmModal, HorizontalGroup, Button } from '@grafana/ui';
|
||||
import { Modal, ConfirmModal, Button } from '@grafana/ui';
|
||||
import { DashboardModel, PanelModel } from '../../state';
|
||||
import { useDashboardDelete } from './useDashboardDelete';
|
||||
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
||||
@ -88,10 +88,10 @@ const ProvisionedDeleteModal = ({ hideModal, provisionedId }: { hideModal(): voi
|
||||
<br />
|
||||
File path: {provisionedId}
|
||||
</p>
|
||||
<HorizontalGroup justify="center">
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={hideModal}>
|
||||
OK
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { FC, useCallback, useState } from 'react';
|
||||
import { Button, Field, Form, HorizontalGroup, Input } from '@grafana/ui';
|
||||
import { Button, Field, Form, Modal, Input } from '@grafana/ui';
|
||||
|
||||
import { RepeatRowSelect } from '../RepeatRowSelect/RepeatRowSelect';
|
||||
|
||||
@ -33,12 +33,12 @@ export const RowOptionsForm: FC<Props> = ({ repeat, title, onUpdate, onCancel })
|
||||
<RepeatRowSelect repeat={newRepeat} onChange={onChangeRepeat} />
|
||||
</Field>
|
||||
|
||||
<HorizontalGroup>
|
||||
<Modal.ButtonRow>
|
||||
<Button type="submit">Update</Button>
|
||||
<Button variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</Modal.ButtonRow>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Button, ConfirmModal, HorizontalGroup, Modal, stylesFactory, useTheme } from '@grafana/ui';
|
||||
import { Button, ConfirmModal, Modal, stylesFactory, useTheme } from '@grafana/ui';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { css } from '@emotion/css';
|
||||
import { DashboardModel } from 'app/features/dashboard/state';
|
||||
@ -89,7 +89,7 @@ const ConfirmPluginDashboardSaveModal: React.FC<SaveDashboardModalProps> = ({ on
|
||||
Use <strong>Save As</strong> to create custom version.
|
||||
</small>
|
||||
</div>
|
||||
<HorizontalGroup justify="center">
|
||||
<Modal.ButtonRow>
|
||||
<SaveDashboardAsButton dashboard={dashboard} onSaveSuccess={onDismiss} />
|
||||
<Button
|
||||
variant="destructive"
|
||||
@ -103,7 +103,7 @@ const ConfirmPluginDashboardSaveModal: React.FC<SaveDashboardModalProps> = ({ on
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</Modal.ButtonRow>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Button, HorizontalGroup, Modal, VerticalGroup } from '@grafana/ui';
|
||||
import { Button, Modal } from '@grafana/ui';
|
||||
import { SaveDashboardButton } from './SaveDashboardButton';
|
||||
import { DashboardModel } from '../../state';
|
||||
import { css } from '@emotion/css';
|
||||
@ -27,24 +27,22 @@ export const UnsavedChangesModal: React.FC<UnsavedChangesModalProps> = ({
|
||||
width: 500px;
|
||||
`}
|
||||
>
|
||||
<VerticalGroup align={'center'} spacing={'md'}>
|
||||
<h4>Do you want to save your changes?</h4>
|
||||
<HorizontalGroup justify="center">
|
||||
<SaveDashboardButton dashboard={dashboard} onSaveSuccess={onSaveSuccess} />
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
onDiscard();
|
||||
onDismiss();
|
||||
}}
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</VerticalGroup>
|
||||
<h5>Do you want to save your changes?</h5>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
onDiscard();
|
||||
onDismiss();
|
||||
}}
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<SaveDashboardButton dashboard={dashboard} onSaveSuccess={onSaveSuccess} />
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Button, HorizontalGroup, Input, Switch, Form, Field, InputControl } from '@grafana/ui';
|
||||
import { Button, Input, Switch, Form, Field, InputControl, Modal } from '@grafana/ui';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
||||
import { SaveDashboardFormProps } from '../types';
|
||||
@ -114,14 +114,14 @@ export const SaveDashboardAsForm: React.FC<SaveDashboardFormProps & { isNew?: bo
|
||||
<Field label="Copy tags">
|
||||
<Switch name="copyTags" ref={register} />
|
||||
</Field>
|
||||
<HorizontalGroup>
|
||||
<Button type="submit" aria-label="Save dashboard button">
|
||||
Save
|
||||
</Button>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
<Button type="submit" aria-label="Save dashboard button">
|
||||
Save
|
||||
</Button>
|
||||
</Modal.ButtonRow>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { Button, Checkbox, Form, HorizontalGroup, TextArea } from '@grafana/ui';
|
||||
import { Button, Checkbox, Form, Modal, TextArea } from '@grafana/ui';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { SaveDashboardFormProps } from '../types';
|
||||
@ -36,7 +36,7 @@ export const SaveDashboardForm: React.FC<SaveDashboardFormProps> = ({ dashboard,
|
||||
>
|
||||
{({ register, errors }) => (
|
||||
<>
|
||||
<div className="gf-form-group">
|
||||
<div>
|
||||
{hasTimeChanged && (
|
||||
<Checkbox
|
||||
label="Save current time range as dashboard default"
|
||||
@ -58,14 +58,14 @@ export const SaveDashboardForm: React.FC<SaveDashboardFormProps> = ({ dashboard,
|
||||
<TextArea name="message" ref={register} placeholder="Add a note to describe your changes." autoFocus />
|
||||
</div>
|
||||
|
||||
<HorizontalGroup>
|
||||
<Button type="submit" aria-label={selectors.pages.SaveDashboardModal.save}>
|
||||
Save
|
||||
</Button>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
<Button type="submit" aria-label={selectors.pages.SaveDashboardModal.save}>
|
||||
Save
|
||||
</Button>
|
||||
</Modal.ButtonRow>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { Button, HorizontalGroup, stylesFactory, TextArea, useTheme, VerticalGroup } from '@grafana/ui';
|
||||
import { Button, Modal, stylesFactory, TextArea, useTheme } from '@grafana/ui';
|
||||
import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
|
||||
import { SaveDashboardFormProps } from '../types';
|
||||
import { AppEvents, GrafanaTheme } from '@grafana/data';
|
||||
@ -29,8 +29,8 @@ export const SaveProvisionedDashboardForm: React.FC<SaveDashboardFormProps> = ({
|
||||
const styles = getStyles(theme);
|
||||
return (
|
||||
<>
|
||||
<VerticalGroup spacing="lg">
|
||||
<small>
|
||||
<div>
|
||||
<div>
|
||||
This dashboard cannot be saved from the Grafana UI because it has been provisioned from another source. Copy
|
||||
the JSON or save it to a file below, then you can update your dashboard in the provisioning source.
|
||||
<br />
|
||||
@ -46,8 +46,7 @@ export const SaveProvisionedDashboardForm: React.FC<SaveDashboardFormProps> = ({
|
||||
</a>{' '}
|
||||
for more information about provisioning.
|
||||
</i>
|
||||
</small>
|
||||
<div>
|
||||
<br /> <br />
|
||||
<strong>File path: </strong> {dashboard.meta.provisionedExternalId}
|
||||
</div>
|
||||
<TextArea
|
||||
@ -58,16 +57,16 @@ export const SaveProvisionedDashboardForm: React.FC<SaveDashboardFormProps> = ({
|
||||
}}
|
||||
className={styles.json}
|
||||
/>
|
||||
<HorizontalGroup>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<CopyToClipboard text={() => dashboardJSON} elType={Button} onSuccess={onCopyToClipboardSuccess}>
|
||||
Copy JSON to clipboard
|
||||
</CopyToClipboard>
|
||||
<Button onClick={saveToFile}>Save JSON to file</Button>
|
||||
<Button variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</VerticalGroup>
|
||||
</Modal.ButtonRow>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { FormEvent, PureComponent } from 'react';
|
||||
import { RadioButtonGroup, Switch, Field, TextArea, ClipboardButton } from '@grafana/ui';
|
||||
import { RadioButtonGroup, Switch, Field, TextArea, ClipboardButton, Modal } from '@grafana/ui';
|
||||
import { SelectableValue, AppEvents } from '@grafana/data';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { appEvents } from 'app/core/core';
|
||||
@ -96,9 +96,11 @@ export class ShareEmbed extends PureComponent<Props, State> {
|
||||
>
|
||||
<TextArea rows={5} value={iframeHtml} onChange={this.onIframeHtmlChange}></TextArea>
|
||||
</Field>
|
||||
<ClipboardButton variant="primary" getText={this.getIframeHtml} onClipboardCopy={this.onIframeHtmlCopy}>
|
||||
Copy to clipboard
|
||||
</ClipboardButton>
|
||||
<Modal.ButtonRow>
|
||||
<ClipboardButton variant="primary" getText={this.getIframeHtml} onClipboardCopy={this.onIframeHtmlCopy}>
|
||||
Copy to clipboard
|
||||
</ClipboardButton>
|
||||
</Modal.ButtonRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { Button, Field, Switch } from '@grafana/ui';
|
||||
import { Button, Field, Modal, Switch } from '@grafana/ui';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { DashboardExporter } from 'app/features/dashboard/components/DashExportModal';
|
||||
import { appEvents } from 'app/core/core';
|
||||
@ -95,17 +95,17 @@ export class ShareExport extends PureComponent<Props, State> {
|
||||
<Field label="Export for sharing externally">
|
||||
<Switch value={shareExternally} onChange={this.onShareExternallyChange} />
|
||||
</Field>
|
||||
<div className="gf-form-button-row">
|
||||
<Button variant="primary" onClick={this.onSaveAsFile}>
|
||||
Save to file
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={this.onViewJson}>
|
||||
View JSON
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
<Button variant="primary" onClick={this.onSaveAsFile}>
|
||||
Save to file
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.ButtonRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Button, ClipboardButton, Icon, Spinner, Select, Input, LinkButton, Field } from '@grafana/ui';
|
||||
import { Button, ClipboardButton, Icon, Spinner, Select, Input, LinkButton, Field, Modal } from '@grafana/ui';
|
||||
import { AppEvents, SelectableValue } from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
@ -233,19 +233,19 @@ export class ShareSnapshot extends PureComponent<Props, State> {
|
||||
<Input type="number" width={21} value={timeoutSeconds} onChange={this.onTimeoutChange} />
|
||||
</Field>
|
||||
|
||||
<div className="gf-form-button-row">
|
||||
<Button variant="primary" disabled={isLoading} onClick={this.createSnapshot()}>
|
||||
Local Snapshot
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
{externalEnabled && (
|
||||
<Button variant="secondary" disabled={isLoading} onClick={this.createSnapshot(true)}>
|
||||
{sharingButtonText}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
<Button variant="primary" disabled={isLoading} onClick={this.createSnapshot()}>
|
||||
Local Snapshot
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.ButtonRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Field, Input, Modal, useStyles } from '@grafana/ui';
|
||||
import { Button, Field, Input, Modal } from '@grafana/ui';
|
||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
||||
import { PanelModel } from '../../../dashboard/state';
|
||||
import { css } from '@emotion/css';
|
||||
import { usePanelSave } from '../../utils/usePanelSave';
|
||||
interface AddLibraryPanelContentsProps {
|
||||
onDismiss: () => void;
|
||||
@ -11,7 +10,6 @@ interface AddLibraryPanelContentsProps {
|
||||
}
|
||||
|
||||
export const AddLibraryPanelContents = ({ panel, initialFolderId, onDismiss }: AddLibraryPanelContentsProps) => {
|
||||
const styles = useStyles(getStyles);
|
||||
const [folderId, setFolderId] = useState(initialFolderId);
|
||||
const [panelTitle, setPanelTitle] = useState(panel.title);
|
||||
const { saveLibraryPanel } = usePanelSave();
|
||||
@ -25,7 +23,7 @@ export const AddLibraryPanelContents = ({ panel, initialFolderId, onDismiss }: A
|
||||
<FolderPicker onChange={({ id }) => setFolderId(id)} initialFolderId={initialFolderId} />
|
||||
</Field>
|
||||
|
||||
<div className={styles.buttons}>
|
||||
<Modal.ButtonRow>
|
||||
<Button
|
||||
onClick={() => {
|
||||
panel.title = panelTitle;
|
||||
@ -37,7 +35,7 @@ export const AddLibraryPanelContents = ({ panel, initialFolderId, onDismiss }: A
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.ButtonRow>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -53,10 +51,3 @@ export const AddLibraryPanelModal: React.FC<Props> = ({ isOpen = false, panel, i
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = () => ({
|
||||
buttons: css`
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
`,
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { FC, useEffect, useMemo, useReducer } from 'react';
|
||||
import { Button, HorizontalGroup, Modal, useStyles } from '@grafana/ui';
|
||||
import { Button, Modal, useStyles } from '@grafana/ui';
|
||||
import { LoadingState } from '@grafana/data';
|
||||
|
||||
import { LibraryPanelDTO } from '../../types';
|
||||
@ -35,14 +35,14 @@ export const DeleteLibraryPanelModal: FC<Props> = ({ libraryPanel, onDismiss, on
|
||||
{connected ? <HasConnectedDashboards dashboardTitles={dashboardTitles} /> : null}
|
||||
{!connected ? <Confirm /> : null}
|
||||
|
||||
<HorizontalGroup>
|
||||
<Button variant="destructive" onClick={onConfirm} disabled={connected}>
|
||||
Delete
|
||||
</Button>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
<Button variant="destructive" onClick={onConfirm} disabled={connected}>
|
||||
Delete
|
||||
</Button>
|
||||
</Modal.ButtonRow>
|
||||
</div>
|
||||
) : null}
|
||||
</Modal>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Button, HorizontalGroup, Icon, Input, Modal, useStyles } from '@grafana/ui';
|
||||
import { Button, Icon, Input, Modal, useStyles } from '@grafana/ui';
|
||||
import { useAsync, useDebounce } from 'react-use';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { usePanelSave } from '../../utils/usePanelSave';
|
||||
@ -98,7 +98,13 @@ export const SaveLibraryPanelModal: React.FC<Props> = ({
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
<HorizontalGroup>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={discardAndClose}>
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
saveLibraryPanel(panel, folderId).then(() => {
|
||||
@ -109,13 +115,7 @@ export const SaveLibraryPanelModal: React.FC<Props> = ({
|
||||
>
|
||||
Update all
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={discardAndClose}>
|
||||
Discard
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</Modal.ButtonRow>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
@ -132,11 +132,11 @@ export const StartModal: FC<StartModalProps> = ({ playlist, onDismiss }) => {
|
||||
onChange={(e) => setAutofit(e.currentTarget.checked)}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
<div className="gf-form-button-row">
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="primary" onClick={onStart}>
|
||||
Start {playlist.name}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user