mirror of https://github.com/grafana/grafana.git
				
				
				
			Buttons: replace usage of .btn classnames (#33226)
* refactor(loginpage): migrate custom button styles to use Button component * refactor(certificationkey): prefer grafana-ui form elements over html elements and classnames * refactor(axisselector): prefer grafana-ui Button component over html button element * refactor(input-datasource): replace use of btn class with grafana-ui components * chore(grafana-ui): delete deprecated ToggleButtonGroup component * refactor: replace btn and cta-form__close class usage with IconButton * chore(closebutton): post master merge use v2 theme * refactor(permissionlist): remove usage of .btn classname * Wip * docs(styling): update styling and theme docs import paths * refactor(alerting): remote btn classnames from TestRuleResult * refactor(apikeys): prefer grafana-ui Button components over btn classNames * refactor(folders): prefer grafana-ui Button components over btn classNames * refactor(teams): prefer grafana-ui Button components over btn classNames * refactor(datasources): prefer grafana-ui Button components over btn classNames * refactor: prefer grafana-ui Button components over btn classNames * Minor style tweak to service buttons * test: update snapshots related to button changes * chore(input-datasource): remove unused import declaration * refactor(loginservicebuttons): rename theme.palette to theme.colors Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
		
							parent
							
								
									6034bf37c6
								
							
						
					
					
						commit
						c809d63065
					
				|  | @ -10,7 +10,7 @@ For styling components, use [Emotion's `css` function](https://emotion.sh/docs/e | |||
| 
 | ||||
| ```tsx | ||||
| import React from 'react'; | ||||
| import { css } from 'emotion'; | ||||
| import { css } from '@emotion/css'; | ||||
| 
 | ||||
| const ComponentA = () => ( | ||||
|   <div | ||||
|  | @ -33,14 +33,13 @@ To access the theme in your styles, use the `useStyles` hook. It provides basic | |||
| import React, { FC } from 'react'; | ||||
| import { GrafanaTheme } from '@grafana/data'; | ||||
| import { useStyles } from '@grafana/ui'; | ||||
| import { css } from 'emotion'; | ||||
| import { css } from '@emotion/css'; | ||||
| 
 | ||||
| const Foo: FC<FooProps> = () => { | ||||
|   const styles = useStyles(getStyles); | ||||
| 
 | ||||
|   // Use styles with classNames | ||||
|   return <div className={styles}>...</div> | ||||
| 
 | ||||
|   return <div className={styles}>...</div>; | ||||
| }; | ||||
| 
 | ||||
| const getStyles = (theme: GrafanaTheme) => css` | ||||
|  | @ -56,15 +55,15 @@ Let's say you need to style a component that has a different background dependin | |||
| 
 | ||||
| ```tsx | ||||
| import React from 'react'; | ||||
| import { css } from 'emotion'; | ||||
| import { css } from '@emotion/css'; | ||||
| import { GrafanaTheme } from '@grafana/data'; | ||||
| import { selectThemeVariant, stylesFactory, useTheme } from '@grafana/ui'; | ||||
| 
 | ||||
| interface ComponentAProps { | ||||
|   isActive: boolean | ||||
|   isActive: boolean; | ||||
| } | ||||
| 
 | ||||
| const ComponentA: React.FC<ComponentAProps> = ({isActive}) => { | ||||
| const ComponentA: React.FC<ComponentAProps> = ({ isActive }) => { | ||||
|   const theme = useTheme(); | ||||
|   const styles = getStyles(theme, isActive); | ||||
| 
 | ||||
|  | @ -76,7 +75,6 @@ const ComponentA: React.FC<ComponentAProps> = ({isActive}) => { | |||
|   ); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| // Mind, that you can pass multiple arguments, theme included | ||||
| const getStyles = stylesFactory((theme: GrafanaTheme, isActive: boolean) => { | ||||
|   const backgroundColor = isActive ? theme.colors.red : theme.colors.blue; | ||||
|  | @ -100,7 +98,7 @@ For class composition, use [Emotion's `cx` function](https://emotion.sh/docs/emo | |||
| 
 | ||||
| ```tsx | ||||
| import React from 'react'; | ||||
| import { css, cx } from 'emotion'; | ||||
| import { css, cx } from '@emotion/css'; | ||||
| 
 | ||||
| interface Props { | ||||
|   className?: string; | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ Here's how to use Grafana themes in React components. | |||
| import React, { FC } from 'react'; | ||||
| import { GrafanaTheme } from '@grafana/data'; | ||||
| import { useStyles } from '@grafana/ui'; | ||||
| import { css } from 'emotion'; | ||||
| import { css } from '@emotion/css'; | ||||
| 
 | ||||
| const getComponentStyles = (theme: GrafanaTheme) => css` | ||||
|   padding: ${theme.spacing.md}; | ||||
|  | @ -57,7 +57,7 @@ const Foo: FC<FooProps> = () => { | |||
| ```tsx | ||||
| import { ThemeContext } from '@grafana/ui'; | ||||
| 
 | ||||
| <ThemeContext.Consumer>{theme => <Foo theme={theme} />}</ThemeContext.Consumer>; | ||||
| <ThemeContext.Consumer>{(theme) => <Foo theme={theme} />}</ThemeContext.Consumer>; | ||||
| ``` | ||||
| 
 | ||||
| #### Using `withTheme` higher-order component (HOC) | ||||
|  | @ -97,9 +97,8 @@ describe('MyComponent', () => { | |||
|     restoreThemeContext(); | ||||
|   }); | ||||
| 
 | ||||
| 
 | ||||
|   it('renders correctly', () => { | ||||
|     const wrapper = mount(<MyComponent />) | ||||
|     const wrapper = mount(<MyComponent />); | ||||
|     expect(wrapper).toMatchSnapshot(); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import { PopoverContentProps } from '../Tooltip/Tooltip'; | |||
| import { Switch } from '../Forms/Legacy/Switch/Switch'; | ||||
| import { css } from '@emotion/css'; | ||||
| import { withTheme, useStyles } from '../../themes'; | ||||
| import { Button } from '../Button'; | ||||
| 
 | ||||
| export interface SeriesColorPickerPopoverProps extends ColorPickerProps, PopoverContentProps { | ||||
|   yaxis?: number; | ||||
|  | @ -70,18 +71,18 @@ export class AxisSelector extends React.PureComponent<AxisSelectorProps, AxisSel | |||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     const leftButtonClass = this.state.yaxis === 1 ? 'btn-primary' : 'btn-inverse'; | ||||
|     const rightButtonClass = this.state.yaxis === 2 ? 'btn-primary' : 'btn-inverse'; | ||||
|     const leftButtonVariant = this.state.yaxis === 1 ? 'primary' : 'secondary'; | ||||
|     const rightButtonVariant = this.state.yaxis === 2 ? 'primary' : 'secondary'; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className="p-b-1"> | ||||
|         <label className="small p-r-1">Y Axis:</label> | ||||
|         <button onClick={this.onToggleAxis} className={'btn btn-small ' + leftButtonClass}> | ||||
|         <Button onClick={this.onToggleAxis} size="sm" variant={leftButtonVariant}> | ||||
|           Left | ||||
|         </button> | ||||
|         <button onClick={this.onToggleAxis} className={'btn btn-small ' + rightButtonClass}> | ||||
|         </Button> | ||||
|         <Button onClick={this.onToggleAxis} size="sm" variant={rightButtonVariant}> | ||||
|           Right | ||||
|         </button> | ||||
|         </Button> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  |  | |||
|  | @ -1,4 +1,8 @@ | |||
| import React, { ChangeEvent, MouseEvent, FC } from 'react'; | ||||
| import { Input } from '../Input/Input'; | ||||
| import { Button } from '../Button'; | ||||
| import { TextArea } from '../TextArea/TextArea'; | ||||
| import { InlineField } from '../Forms/InlineField'; | ||||
| 
 | ||||
| interface Props { | ||||
|   label: string; | ||||
|  | @ -6,35 +10,22 @@ interface Props { | |||
|   placeholder: string; | ||||
| 
 | ||||
|   onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void; | ||||
|   onClick: (event: MouseEvent<HTMLAnchorElement>) => void; | ||||
|   onClick: (event: MouseEvent<HTMLButtonElement>) => void; | ||||
| } | ||||
| 
 | ||||
| export const CertificationKey: FC<Props> = ({ hasCert, label, onChange, onClick, placeholder }) => { | ||||
|   return ( | ||||
|     <div className="gf-form-inline"> | ||||
|       <div className="gf-form gf-form--v-stretch"> | ||||
|         <label className="gf-form-label width-7">{label}</label> | ||||
|       </div> | ||||
|       {!hasCert && ( | ||||
|         <div className="gf-form gf-form--grow"> | ||||
|           <textarea | ||||
|             rows={7} | ||||
|             className="gf-form-input gf-form-textarea" | ||||
|             onChange={onChange} | ||||
|             placeholder={placeholder} | ||||
|             required | ||||
|           /> | ||||
|         </div> | ||||
|     <InlineField label={label} labelWidth={14}> | ||||
|       {hasCert ? ( | ||||
|         <> | ||||
|           <Input type="text" disabled value="configured" width={24} /> | ||||
|           <Button variant="secondary" onClick={onClick} style={{ marginLeft: 4 }}> | ||||
|             Reset | ||||
|           </Button> | ||||
|         </> | ||||
|       ) : ( | ||||
|         <TextArea rows={7} onChange={onChange} placeholder={placeholder} required /> | ||||
|       )} | ||||
| 
 | ||||
|       {hasCert && ( | ||||
|         <div className="gf-form"> | ||||
|           <input type="text" className="gf-form-input max-width-12" disabled value="configured" /> | ||||
|           <a className="btn btn-secondary gf-form-btn" onClick={onClick}> | ||||
|             reset | ||||
|           </a> | ||||
|         </div> | ||||
|       )} | ||||
|     </div> | ||||
|     </InlineField> | ||||
|   ); | ||||
| }; | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ export const TLSAuthSettings: React.FC<HttpSettingsBaseProps> = ({ dataSourceCon | |||
|   const hasTLSClientKey = dataSourceConfig.secureJsonFields && dataSourceConfig.secureJsonFields.tlsClientKey; | ||||
|   const hasServerName = dataSourceConfig.jsonData && dataSourceConfig.jsonData.serverName; | ||||
| 
 | ||||
|   const onResetClickFactory = (field: string) => (event: React.MouseEvent<HTMLAnchorElement>) => { | ||||
|   const onResetClickFactory = (field: string) => (event: React.MouseEvent<HTMLButtonElement>) => { | ||||
|     event.preventDefault(); | ||||
|     const newSecureJsonFields: KeyValue<boolean> = { ...dataSourceConfig.secureJsonFields }; | ||||
|     newSecureJsonFields[field] = false; | ||||
|  |  | |||
|  | @ -1,50 +0,0 @@ | |||
| import React from 'react'; | ||||
| import { action } from '@storybook/addon-actions'; | ||||
| 
 | ||||
| import { ToggleButton, ToggleButtonGroup } from './ToggleButtonGroup'; | ||||
| import { UseState } from '../../utils/storybook/UseState'; | ||||
| import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; | ||||
| 
 | ||||
| export default { | ||||
|   title: 'Forms/Legacy/ToggleButtonGroup', | ||||
|   component: ToggleButtonGroup, | ||||
|   decorators: [withCenteredStory], | ||||
| }; | ||||
| 
 | ||||
| const options = [ | ||||
|   { value: 'first', label: 'First' }, | ||||
|   { value: 'second', label: 'Second' }, | ||||
|   { value: 'third', label: 'Third' }, | ||||
| ]; | ||||
| 
 | ||||
| export const basic = () => { | ||||
|   return ( | ||||
|     <UseState | ||||
|       initialState={{ | ||||
|         value: 'first', | ||||
|       }} | ||||
|     > | ||||
|       {(value, updateValue) => { | ||||
|         return ( | ||||
|           <ToggleButtonGroup label="Options"> | ||||
|             {options.map((option, index) => { | ||||
|               return ( | ||||
|                 <ToggleButton | ||||
|                   key={`${option.value}-${index}`} | ||||
|                   value={option.value} | ||||
|                   onChange={(newValue) => { | ||||
|                     action('on change')(newValue); | ||||
|                     updateValue({ value: newValue }); | ||||
|                   }} | ||||
|                   selected={value.value === option.value} | ||||
|                 > | ||||
|                   {option.label} | ||||
|                 </ToggleButton> | ||||
|               ); | ||||
|             })} | ||||
|           </ToggleButtonGroup> | ||||
|         ); | ||||
|       }} | ||||
|     </UseState> | ||||
|   ); | ||||
| }; | ||||
|  | @ -1,77 +0,0 @@ | |||
| import React, { FC, ReactNode, PureComponent } from 'react'; | ||||
| import classNames from 'classnames'; | ||||
| import { Tooltip } from '../Tooltip/Tooltip'; | ||||
| import { deprecationWarning } from '@grafana/data'; | ||||
| 
 | ||||
| interface ToggleButtonGroupProps { | ||||
|   label?: string; | ||||
|   children: JSX.Element[]; | ||||
|   transparent?: boolean; | ||||
|   width?: number; | ||||
| } | ||||
| 
 | ||||
| /** @deprecated */ | ||||
| export class ToggleButtonGroup extends PureComponent<ToggleButtonGroupProps> { | ||||
|   render() { | ||||
|     const { children, label, transparent, width } = this.props; | ||||
|     const labelClasses = classNames('gf-form-label', { | ||||
|       'gf-form-label--transparent': transparent, | ||||
|       [`width-${width}`]: width, | ||||
|     }); | ||||
|     const buttonGroupClasses = classNames('toggle-button-group', { | ||||
|       'toggle-button-group--transparent': transparent, | ||||
|       'toggle-button-group--padded': width, // Add extra padding to compensate for buttons border
 | ||||
|     }); | ||||
| 
 | ||||
|     deprecationWarning('ToggleButtonGroup', 'ToggleButtonGroup', 'RadioButtonGroup'); | ||||
| 
 | ||||
|     return ( | ||||
|       <div className="gf-form gf-form--align-center"> | ||||
|         {label && <label className={labelClasses}>{label}</label>} | ||||
|         <div className={buttonGroupClasses}>{children}</div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| interface ToggleButtonProps { | ||||
|   onChange?: (value: any) => void; | ||||
|   selected?: boolean; | ||||
|   value: any; | ||||
|   className?: string; | ||||
|   children: ReactNode; | ||||
|   tooltip?: string; | ||||
| } | ||||
| 
 | ||||
| export const ToggleButton: FC<ToggleButtonProps> = ({ | ||||
|   children, | ||||
|   selected, | ||||
|   className = '', | ||||
|   value = null, | ||||
|   tooltip, | ||||
|   onChange, | ||||
| }) => { | ||||
|   const onClick = (event: React.SyntheticEvent) => { | ||||
|     event.stopPropagation(); | ||||
|     if (!selected && onChange) { | ||||
|       onChange(value); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const btnClassName = `btn ${className}${selected ? ' active' : ''}`; | ||||
|   const button = ( | ||||
|     <button className={btnClassName} onClick={onClick}> | ||||
|       <span>{children}</span> | ||||
|     </button> | ||||
|   ); | ||||
| 
 | ||||
|   if (tooltip) { | ||||
|     return ( | ||||
|       <Tooltip content={tooltip} placement="bottom"> | ||||
|         {button} | ||||
|       </Tooltip> | ||||
|     ); | ||||
|   } else { | ||||
|     return button; | ||||
|   } | ||||
| }; | ||||
|  | @ -110,7 +110,6 @@ export { LogRows } from './Logs/LogRows'; | |||
| export { getLogRowStyles } from './Logs/getLogRowStyles'; | ||||
| export { DataLinkButton } from './DataLinks/DataLinkButton'; | ||||
| export { FieldLinkList } from './DataLinks/FieldLinkList'; | ||||
| export { ToggleButtonGroup, ToggleButton } from './ToggleButtonGroup/ToggleButtonGroup'; | ||||
| // Panel editors
 | ||||
| export { FullWidthButtonContainer } from './Button/FullWidthButtonContainer'; | ||||
| export { ClickOutsideWrapper } from './ClickOutsideWrapper/ClickOutsideWrapper'; | ||||
|  |  | |||
|  | @ -76,8 +76,11 @@ export type IconName = | |||
|   | 'gf-interpolation-step-after' | ||||
|   | 'gf-interpolation-step-before' | ||||
|   | 'gf-logs' | ||||
|   | 'github' | ||||
|   | 'gitlab' | ||||
|   | 'grafana' | ||||
|   | 'graph-bar' | ||||
|   | 'google' | ||||
|   | 'heart-break' | ||||
|   | 'heart' | ||||
|   | 'history' | ||||
|  | @ -90,10 +93,12 @@ export type IconName = | |||
|   | 'link' | ||||
|   | 'list-ul' | ||||
|   | 'lock' | ||||
|   | 'microsoft' | ||||
|   | 'minus-circle' | ||||
|   | 'minus' | ||||
|   | 'mobile-android' | ||||
|   | 'monitor' | ||||
|   | 'okta' | ||||
|   | 'palette' | ||||
|   | 'panel-add' | ||||
|   | 'pause' | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ import React, { PureComponent } from 'react'; | |||
| import { InputDatasource, describeDataFrame } from './InputDatasource'; | ||||
| import { InputQuery, InputOptions } from './types'; | ||||
| 
 | ||||
| import { InlineFormLabel, LegacyForms, TableInputCSV, Icon } from '@grafana/ui'; | ||||
| const { Select } = LegacyForms; | ||||
| import { Select, TableInputCSV, LinkButton, Icon, InlineField } from '@grafana/ui'; | ||||
| import { DataFrame, toCSV, SelectableValue, MutableDataFrame, QueryEditorProps } from '@grafana/data'; | ||||
| 
 | ||||
| import { dataFrameToCSV } from './utils'; | ||||
|  | @ -68,21 +67,19 @@ export class InputQueryEditor extends PureComponent<Props, State> { | |||
|     const selected = query.data ? options[0] : options[1]; | ||||
|     return ( | ||||
|       <div> | ||||
|         <div className="gf-form"> | ||||
|           <InlineFormLabel width={4}>Data</InlineFormLabel> | ||||
|           <Select width={6} options={options} value={selected} onChange={this.onSourceChange} /> | ||||
| 
 | ||||
|           <div className="btn btn-link"> | ||||
|         <InlineField label="Data" labelWidth={8}> | ||||
|           <> | ||||
|             <Select width={20} options={options} value={selected} onChange={this.onSourceChange} /> | ||||
|             {query.data ? ( | ||||
|               describeDataFrame(query.data) | ||||
|               <div style={{ alignSelf: 'center' }}>{describeDataFrame(query.data)}</div> | ||||
|             ) : ( | ||||
|               <a href={`datasources/edit/${id}/`}> | ||||
|               <LinkButton variant="link" href={`datasources/edit/${id}/`}> | ||||
|                 {name}: {describeDataFrame(datasource.data)}    | ||||
|                 <Icon name="pen" /> | ||||
|               </a> | ||||
|               </LinkButton> | ||||
|             )} | ||||
|           </div> | ||||
|         </div> | ||||
|           </> | ||||
|         </InlineField> | ||||
|         {query.data && <TableInputCSV text={text} onSeriesParsed={this.onSeriesParsed} width={'100%'} height={200} />} | ||||
|       </div> | ||||
|     ); | ||||
|  |  | |||
|  | @ -0,0 +1,20 @@ | |||
| import React from 'react'; | ||||
| import { css } from '@emotion/css'; | ||||
| import { IconButton, useStyles2 } from '@grafana/ui'; | ||||
| import { GrafanaThemeV2 } from '@grafana/data'; | ||||
| 
 | ||||
| type Props = { | ||||
|   onClick: () => void; | ||||
| }; | ||||
| 
 | ||||
| export const CloseButton: React.FC<Props> = ({ onClick }) => { | ||||
|   const styles = useStyles2(getStyles); | ||||
|   return <IconButton className={styles} name="times" onClick={onClick} />; | ||||
| }; | ||||
| 
 | ||||
| const getStyles = (theme: GrafanaThemeV2) => | ||||
|   css` | ||||
|     position: absolute; | ||||
|     right: ${theme.spacing(0.5)}; | ||||
|     top: ${theme.spacing(1)}; | ||||
|   `;
 | ||||
|  | @ -40,28 +40,26 @@ export const LoginPage: FC = () => { | |||
|             {!isChangingPassword && ( | ||||
|               <InnerBox> | ||||
|                 {!disableLoginForm && ( | ||||
|                   <> | ||||
|                     <LoginForm | ||||
|                       onSubmit={login} | ||||
|                       loginHint={loginHint} | ||||
|                       passwordHint={passwordHint} | ||||
|                       isLoggingIn={isLoggingIn} | ||||
|                     > | ||||
|                       {!(ldapEnabled || authProxyEnabled) ? ( | ||||
|                         <HorizontalGroup justify="flex-end"> | ||||
|                           <LinkButton | ||||
|                             className={forgottenPasswordStyles} | ||||
|                             variant="link" | ||||
|                             href={`${config.appSubUrl}/user/password/send-reset-email`} | ||||
|                           > | ||||
|                             Forgot your password? | ||||
|                           </LinkButton> | ||||
|                         </HorizontalGroup> | ||||
|                       ) : ( | ||||
|                         <></> | ||||
|                       )} | ||||
|                     </LoginForm> | ||||
|                   </> | ||||
|                   <LoginForm | ||||
|                     onSubmit={login} | ||||
|                     loginHint={loginHint} | ||||
|                     passwordHint={passwordHint} | ||||
|                     isLoggingIn={isLoggingIn} | ||||
|                   > | ||||
|                     {!(ldapEnabled || authProxyEnabled) ? ( | ||||
|                       <HorizontalGroup justify="flex-end"> | ||||
|                         <LinkButton | ||||
|                           className={forgottenPasswordStyles} | ||||
|                           variant="link" | ||||
|                           href={`${config.appSubUrl}/user/password/send-reset-email`} | ||||
|                         > | ||||
|                           Forgot your password? | ||||
|                         </LinkButton> | ||||
|                       </HorizontalGroup> | ||||
|                     ) : ( | ||||
|                       <></> | ||||
|                     )} | ||||
|                   </LoginForm> | ||||
|                 )} | ||||
|                 <LoginServiceButtons /> | ||||
|                 {!disableUserSignUp && <UserSignup />} | ||||
|  |  | |||
|  | @ -1,89 +1,99 @@ | |||
| import React from 'react'; | ||||
| import config from 'app/core/config'; | ||||
| import { css, cx } from '@emotion/css'; | ||||
| import { useStyles } from '@grafana/ui'; | ||||
| import { GrafanaTheme } from '@grafana/data'; | ||||
| 
 | ||||
| const loginServices: () => LoginServices = () => { | ||||
|   const oauthEnabled = !!config.oauth; | ||||
| 
 | ||||
|   return { | ||||
|     saml: { | ||||
|       enabled: config.samlEnabled, | ||||
|       name: 'SAML', | ||||
|       className: 'github', | ||||
|       icon: 'key', | ||||
|     }, | ||||
|     google: { | ||||
|       enabled: oauthEnabled && config.oauth.google, | ||||
|       name: 'Google', | ||||
|     }, | ||||
|     azuread: { | ||||
|       enabled: oauthEnabled && config.oauth.azuread, | ||||
|       name: 'Microsoft', | ||||
|     }, | ||||
|     github: { | ||||
|       enabled: oauthEnabled && config.oauth.github, | ||||
|       name: 'GitHub', | ||||
|     }, | ||||
|     gitlab: { | ||||
|       enabled: oauthEnabled && config.oauth.gitlab, | ||||
|       name: 'GitLab', | ||||
|     }, | ||||
|     grafanacom: { | ||||
|       enabled: oauthEnabled && config.oauth.grafana_com, | ||||
|       name: 'Grafana.com', | ||||
|       hrefName: 'grafana_com', | ||||
|       icon: 'grafana_com', | ||||
|     }, | ||||
|     okta: { | ||||
|       enabled: oauthEnabled && config.oauth.okta, | ||||
|       name: 'Okta', | ||||
|     }, | ||||
|     oauth: { | ||||
|       enabled: oauthEnabled && config.oauth.generic_oauth, | ||||
|       name: oauthEnabled && config.oauth.generic_oauth ? config.oauth.generic_oauth.name : 'OAuth', | ||||
|       icon: 'sign-in', | ||||
|       hrefName: 'generic_oauth', | ||||
|     }, | ||||
|   }; | ||||
| }; | ||||
| import { Icon, IconName, LinkButton, useStyles, useTheme2, VerticalGroup } from '@grafana/ui'; | ||||
| import { GrafanaTheme, GrafanaThemeV2 } from '@grafana/data'; | ||||
| import { pickBy } from 'lodash'; | ||||
| 
 | ||||
| export interface LoginService { | ||||
|   bgColor: string; | ||||
|   enabled: boolean; | ||||
|   name: string; | ||||
|   hrefName?: string; | ||||
|   icon?: string; | ||||
|   className?: string; | ||||
|   icon: IconName; | ||||
| } | ||||
| 
 | ||||
| export interface LoginServices { | ||||
|   [key: string]: LoginService; | ||||
| } | ||||
| 
 | ||||
| const loginServices: () => LoginServices = () => { | ||||
|   const oauthEnabled = !!config.oauth; | ||||
| 
 | ||||
|   return { | ||||
|     saml: { | ||||
|       bgColor: '#464646', | ||||
|       enabled: config.samlEnabled, | ||||
|       name: 'SAML', | ||||
|       icon: 'key-skeleton-alt', | ||||
|     }, | ||||
|     google: { | ||||
|       bgColor: '#e84d3c', | ||||
|       enabled: oauthEnabled && config.oauth.google, | ||||
|       name: 'Google', | ||||
|       icon: 'google', | ||||
|     }, | ||||
|     azuread: { | ||||
|       bgColor: '#2f2f2f', | ||||
|       enabled: oauthEnabled && config.oauth.azuread, | ||||
|       name: 'Microsoft', | ||||
|       icon: 'microsoft', | ||||
|     }, | ||||
|     github: { | ||||
|       bgColor: '#464646', | ||||
|       enabled: oauthEnabled && config.oauth.github, | ||||
|       name: 'GitHub', | ||||
|       icon: 'github', | ||||
|     }, | ||||
|     gitlab: { | ||||
|       bgColor: '#fc6d26', | ||||
|       enabled: oauthEnabled && config.oauth.gitlab, | ||||
|       name: 'GitLab', | ||||
|       icon: 'gitlab', | ||||
|     }, | ||||
|     grafanacom: { | ||||
|       bgColor: '#262628', | ||||
|       enabled: oauthEnabled && config.oauth.grafana_com, | ||||
|       name: 'Grafana.com', | ||||
|       hrefName: 'grafana_com', | ||||
|       icon: 'grafana', | ||||
|     }, | ||||
|     okta: { | ||||
|       bgColor: '#2f2f2f', | ||||
|       enabled: oauthEnabled && config.oauth.okta, | ||||
|       name: 'Okta', | ||||
|       icon: 'okta', | ||||
|     }, | ||||
|     oauth: { | ||||
|       bgColor: '#262628', | ||||
|       enabled: oauthEnabled && config.oauth.generic_oauth, | ||||
|       name: oauthEnabled && config.oauth.generic_oauth ? config.oauth.generic_oauth.name : 'OAuth', | ||||
|       icon: 'signin', | ||||
|       hrefName: 'generic_oauth', | ||||
|     }, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const getServiceStyles = (theme: GrafanaTheme) => { | ||||
|   return { | ||||
|     container: css` | ||||
|       width: 100%; | ||||
|       text-align: center; | ||||
|     `,
 | ||||
|     button: css` | ||||
|       color: #d8d9da; | ||||
|       margin: 0 0 ${theme.spacing.md}; | ||||
|       width: 100%; | ||||
|       &:hover { | ||||
|         color: #fff; | ||||
|       } | ||||
|       position: relative; | ||||
|     `,
 | ||||
|     buttonIcon: css` | ||||
|       position: absolute; | ||||
|       left: ${theme.spacing.sm}; | ||||
|       top: 50%; | ||||
|       transform: translateY(-50%); | ||||
|     `,
 | ||||
|     divider: { | ||||
|       base: css` | ||||
|         float: left; | ||||
|         width: 100%; | ||||
|         margin: 0 25% ${theme.spacing.md} 25%; | ||||
|         color: ${theme.colors.text}; | ||||
|         display: flex; | ||||
|         margin-bottom: ${theme.spacing.sm}; | ||||
|         justify-content: space-between; | ||||
|         text-align: center; | ||||
|         color: ${theme.colors.text}; | ||||
|         width: 100%; | ||||
|       `,
 | ||||
|       line: css` | ||||
|         width: 100px; | ||||
|  | @ -114,38 +124,46 @@ const LoginDivider = () => { | |||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export const LoginServiceButtons = () => { | ||||
|   const styles = useStyles(getServiceStyles); | ||||
|   const keyNames = Object.keys(loginServices()); | ||||
|   const serviceElementsEnabled = keyNames.filter((key) => { | ||||
|     const service: LoginService = loginServices()[key]; | ||||
|     return service.enabled; | ||||
|   }); | ||||
| function getButtonStyleFor(service: LoginService, styles: ReturnType<typeof getServiceStyles>, theme: GrafanaThemeV2) { | ||||
|   return cx( | ||||
|     styles.button, | ||||
|     css` | ||||
|       background-color: ${service.bgColor}; | ||||
|       color: ${theme.colors.getContrastText(service.bgColor)}; | ||||
| 
 | ||||
|   if (serviceElementsEnabled.length === 0) { | ||||
|     return null; | ||||
|       &:hover { | ||||
|         background-color: ${theme.colors.emphasize(service.bgColor, 0.15)}; | ||||
|         box-shadow: ${theme.shadows.z1}; | ||||
|       } | ||||
|     ` | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export const LoginServiceButtons = () => { | ||||
|   const enabledServices = pickBy(loginServices(), (service) => service.enabled); | ||||
|   const hasServices = Object.keys(enabledServices).length > 0; | ||||
|   const theme = useTheme2(); | ||||
|   const styles = useStyles(getServiceStyles); | ||||
| 
 | ||||
|   if (hasServices) { | ||||
|     return ( | ||||
|       <VerticalGroup> | ||||
|         <LoginDivider /> | ||||
|         {Object.entries(enabledServices).map(([key, service]) => ( | ||||
|           <LinkButton | ||||
|             key={key} | ||||
|             className={getButtonStyleFor(service, styles, theme)} | ||||
|             href={`login/${service.hrefName ? service.hrefName : key}`} | ||||
|             target="_self" | ||||
|             fullWidth | ||||
|           > | ||||
|             <Icon className={styles.buttonIcon} name={service.icon} /> | ||||
|             Sign in with {service.name} | ||||
|           </LinkButton> | ||||
|         ))} | ||||
|       </VerticalGroup> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   const serviceElements = serviceElementsEnabled.map((key) => { | ||||
|     const service: LoginService = loginServices()[key]; | ||||
|     return ( | ||||
|       <a | ||||
|         key={key} | ||||
|         className={cx(`btn btn-medium btn-service btn-service--${service.className || key}`, styles.button)} | ||||
|         href={`login/${service.hrefName ? service.hrefName : key}`} | ||||
|         target="_self" | ||||
|       > | ||||
|         <i className={`btn-service-icon fa fa-${service.icon ? service.icon : key}`} /> | ||||
|         Sign in with {service.name} | ||||
|       </a> | ||||
|     ); | ||||
|   }); | ||||
| 
 | ||||
|   const divider = LoginDivider(); | ||||
|   return ( | ||||
|     <> | ||||
|       {divider} | ||||
|       <div className={styles.container}>{serviceElements}</div> | ||||
|     </> | ||||
|   ); | ||||
|   return null; | ||||
| }; | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ export const UserSignup: FC<{}> = () => { | |||
|   return ( | ||||
|     <VerticalGroup | ||||
|       className={css` | ||||
|         margin-top: 8px; | ||||
|         margin-top: 16px; | ||||
|       `}
 | ||||
|     > | ||||
|       <span>New to Grafana?</span> | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import { css } from '@emotion/css'; | |||
| import config from 'app/core/config'; | ||||
| import { UserPicker } from 'app/core/components/Select/UserPicker'; | ||||
| import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker'; | ||||
| import { Button, Form, HorizontalGroup, Icon, Select, stylesFactory } from '@grafana/ui'; | ||||
| import { Button, Form, HorizontalGroup, Select, stylesFactory } from '@grafana/ui'; | ||||
| import { GrafanaTheme, SelectableValue } from '@grafana/data'; | ||||
| import { User } from 'app/types'; | ||||
| import { | ||||
|  | @ -14,6 +14,7 @@ import { | |||
|   NewDashboardAclItem, | ||||
|   OrgRole, | ||||
| } from 'app/types/acl'; | ||||
| import { CloseButton } from '../CloseButton/CloseButton'; | ||||
| 
 | ||||
| export interface Props { | ||||
|   onAddPermission: (item: NewDashboardAclItem) => void; | ||||
|  | @ -93,9 +94,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> { | |||
| 
 | ||||
|     return ( | ||||
|       <div className="cta-form"> | ||||
|         <button className="cta-form__close btn btn-transparent" onClick={onCancel}> | ||||
|           <Icon name="times" /> | ||||
|         </button> | ||||
|         <CloseButton onClick={onCancel} /> | ||||
|         <h5>Add Permission For</h5> | ||||
|         <Form maxWidth="none" onSubmit={this.onSubmit}> | ||||
|           {() => ( | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import React, { Component } from 'react'; | ||||
| import { Select, Icon } from '@grafana/ui'; | ||||
| import { Select, Icon, Button } from '@grafana/ui'; | ||||
| import { dashboardPermissionLevels } from 'app/types/acl'; | ||||
| 
 | ||||
| export interface Props { | ||||
|  | @ -33,9 +33,7 @@ export default class DisabledPermissionListItem extends Component<Props, any> { | |||
|           </div> | ||||
|         </td> | ||||
|         <td> | ||||
|           <button className="btn btn-inverse btn-small"> | ||||
|             <Icon name="lock" /> | ||||
|           </button> | ||||
|           <Button size="sm" disabled icon="lock" /> | ||||
|         </td> | ||||
|       </tr> | ||||
|     ); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import React, { PureComponent } from 'react'; | ||||
| import { Select, Icon } from '@grafana/ui'; | ||||
| import { Select, Icon, Button } from '@grafana/ui'; | ||||
| import { SelectableValue } from '@grafana/data'; | ||||
| import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl'; | ||||
| import { FolderInfo } from 'app/types'; | ||||
|  | @ -85,13 +85,9 @@ export default class PermissionsListItem extends PureComponent<Props> { | |||
|         </td> | ||||
|         <td> | ||||
|           {!item.inherited ? ( | ||||
|             <a className="btn btn-danger btn-small" onClick={this.onRemoveItem}> | ||||
|               <Icon name="times" style={{ marginBottom: 0 }} /> | ||||
|             </a> | ||||
|             <Button size="sm" variant="destructive" icon="times" onClick={this.onRemoveItem} /> | ||||
|           ) : ( | ||||
|             <button className="btn btn-inverse btn-small"> | ||||
|               <Icon name="lock" style={{ marginBottom: '3px' }} /> | ||||
|             </button> | ||||
|             <Button size="sm" disabled icon="times" /> | ||||
|           )} | ||||
|         </td> | ||||
|       </tr> | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; | |||
| import { hot } from 'react-hot-loader'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { NavModel } from '@grafana/data'; | ||||
| import { Alert, LegacyForms } from '@grafana/ui'; | ||||
| import { Alert, Button, LegacyForms } from '@grafana/ui'; | ||||
| const { FormField } = LegacyForms; | ||||
| import { getNavModel } from 'app/core/selectors/navModel'; | ||||
| import config from 'app/core/config'; | ||||
|  | @ -122,9 +122,7 @@ export class LdapPage extends PureComponent<Props, State> { | |||
|                       name="username" | ||||
|                       defaultValue={queryParams.username} | ||||
|                     /> | ||||
|                     <button type="submit" className="btn btn-primary"> | ||||
|                       Run | ||||
|                     </button> | ||||
|                     <Button type="submit">Run</Button> | ||||
|                   </form> | ||||
|                 </div> | ||||
|                 {userError && userError.title && ( | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import React, { PureComponent } from 'react'; | ||||
| import { dateTimeFormat } from '@grafana/data'; | ||||
| import { Spinner } from '@grafana/ui'; | ||||
| import { Button, Spinner } from '@grafana/ui'; | ||||
| import { SyncInfo } from 'app/types'; | ||||
| 
 | ||||
| interface Props { | ||||
|  | @ -31,10 +31,10 @@ export class LdapSyncInfo extends PureComponent<Props, State> { | |||
|       <> | ||||
|         <h3 className="page-heading"> | ||||
|           LDAP Synchronisation | ||||
|           <button className={`btn btn-secondary pull-right`} onClick={this.handleSyncClick} hidden={true}> | ||||
|           <Button className="pull-right" onClick={this.handleSyncClick} hidden> | ||||
|             <span className="btn-title">Bulk-sync now</span> | ||||
|             {isSyncing && <Spinner inline={true} />} | ||||
|           </button> | ||||
|           </Button> | ||||
|         </h3> | ||||
|         <div className="gf-form-group"> | ||||
|           <div className="gf-form"> | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import React, { PureComponent } from 'react'; | ||||
| import { LoadingPlaceholder, JSONFormatter, Icon } from '@grafana/ui'; | ||||
| 
 | ||||
| import { LoadingPlaceholder, JSONFormatter, Icon, HorizontalGroup } from '@grafana/ui'; | ||||
| import appEvents from 'app/core/app_events'; | ||||
| import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard'; | ||||
| import { DashboardModel, PanelModel } from '../dashboard/state'; | ||||
|  | @ -106,16 +105,12 @@ export class TestRuleResult extends PureComponent<Props, State> { | |||
|     return ( | ||||
|       <> | ||||
|         <div className="pull-right"> | ||||
|           <button className="btn btn-transparent btn-p-x-0 m-r-1" onClick={this.onToggleExpand}> | ||||
|             {this.renderExpandCollapse()} | ||||
|           </button> | ||||
|           <CopyToClipboard | ||||
|             className="btn btn-transparent btn-p-x-0" | ||||
|             text={this.getTextForClipboard} | ||||
|             onSuccess={this.onClipboardSuccess} | ||||
|           > | ||||
|             <Icon name="copy" /> Copy to Clipboard | ||||
|           </CopyToClipboard> | ||||
|           <HorizontalGroup spacing="md"> | ||||
|             <div onClick={this.onToggleExpand}>{this.renderExpandCollapse()}</div> | ||||
|             <CopyToClipboard elType="div" text={this.getTextForClipboard} onSuccess={this.onClipboardSuccess}> | ||||
|               <Icon name="copy" /> Copy to Clipboard | ||||
|             </CopyToClipboard> | ||||
|           </HorizontalGroup> | ||||
|         </div> | ||||
| 
 | ||||
|         <JSONFormatter json={testRuleResponse} open={openNodes} onDidRender={this.setFormattedJson} /> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import React, { FC } from 'react'; | ||||
| 
 | ||||
| import { Button } from '@grafana/ui'; | ||||
| import { FilterInput } from '../../core/components/FilterInput/FilterInput'; | ||||
| 
 | ||||
| interface Props { | ||||
|  | @ -17,9 +17,9 @@ export const ApiKeysActionBar: FC<Props> = ({ searchQuery, disabled, onAddClick, | |||
|       </div> | ||||
| 
 | ||||
|       <div className="page-action-bar__spacer" /> | ||||
|       <button className="btn btn-primary pull-right" onClick={onAddClick} disabled={disabled}> | ||||
|       <Button className="pull-right" onClick={onAddClick} disabled={disabled}> | ||||
|         Add API key | ||||
|       </button> | ||||
|       </Button> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| import React, { ChangeEvent, FC, FormEvent, useEffect, useState } from 'react'; | ||||
| import { EventsWithValidation, Icon, InlineFormLabel, LegacyForms, ValidationEvents } from '@grafana/ui'; | ||||
| import { EventsWithValidation, InlineFormLabel, LegacyForms, ValidationEvents, Button } from '@grafana/ui'; | ||||
| import { NewApiKey, OrgRole } from '../../types'; | ||||
| import { rangeUtil } from '@grafana/data'; | ||||
| import { SlideDown } from '../../core/components/Animations/SlideDown'; | ||||
| import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; | ||||
| 
 | ||||
| const { Input } = LegacyForms; | ||||
| 
 | ||||
|  | @ -65,9 +66,7 @@ export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded }) => { | |||
|   return ( | ||||
|     <SlideDown in={show}> | ||||
|       <div className="gf-form-inline cta-form"> | ||||
|         <button className="cta-form__close btn btn-transparent" onClick={onClose}> | ||||
|           <Icon name="times" /> | ||||
|         </button> | ||||
|         <CloseButton onClick={onClose} /> | ||||
|         <form className="gf-form-group" onSubmit={onSubmit}> | ||||
|           <h5>Add API Key</h5> | ||||
|           <div className="gf-form-inline"> | ||||
|  | @ -101,7 +100,7 @@ export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded }) => { | |||
|               /> | ||||
|             </div> | ||||
|             <div className="gf-form"> | ||||
|               <button className="btn gf-form-btn btn-primary">Add</button> | ||||
|               <Button>Add</Button> | ||||
|             </div> | ||||
|           </div> | ||||
|         </form> | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import React, { FC } from 'react'; | ||||
| import { PluginDashboard } from '../../types'; | ||||
| import { Icon } from '@grafana/ui'; | ||||
| import { Button, Icon } from '@grafana/ui'; | ||||
| 
 | ||||
| export interface Props { | ||||
|   dashboards: PluginDashboard[]; | ||||
|  | @ -31,18 +31,16 @@ const DashboardsTable: FC<Props> = ({ dashboards, onImport, onRemove }) => { | |||
|               </td> | ||||
|               <td style={{ textAlign: 'right' }}> | ||||
|                 {!dashboard.imported ? ( | ||||
|                   <button className="btn btn-secondary btn-small" onClick={() => onImport(dashboard, false)}> | ||||
|                   <Button variant="secondary" size="sm" onClick={() => onImport(dashboard, false)}> | ||||
|                     Import | ||||
|                   </button> | ||||
|                   </Button> | ||||
|                 ) : ( | ||||
|                   <button className="btn btn-secondary btn-small" onClick={() => onImport(dashboard, true)}> | ||||
|                   <Button variant="secondary" size="sm" onClick={() => onImport(dashboard, true)}> | ||||
|                     {buttonText(dashboard)} | ||||
|                   </button> | ||||
|                   </Button> | ||||
|                 )} | ||||
|                 {dashboard.imported && ( | ||||
|                   <button className="btn btn-danger btn-small" onClick={() => onRemove(dashboard)}> | ||||
|                     <Icon name="trash-alt" /> | ||||
|                   </button> | ||||
|                   <Button icon="trash-alt" variant="destructive" size="sm" onClick={() => onRemove(dashboard)} /> | ||||
|                 )} | ||||
|               </td> | ||||
|             </tr> | ||||
|  |  | |||
|  | @ -35,12 +35,13 @@ exports[`Render should render table 1`] = ` | |||
|           } | ||||
|         } | ||||
|       > | ||||
|         <button | ||||
|           className="btn btn-secondary btn-small" | ||||
|         <Button | ||||
|           onClick={[Function]} | ||||
|           size="sm" | ||||
|           variant="secondary" | ||||
|         > | ||||
|           Import | ||||
|         </button> | ||||
|         </Button> | ||||
|       </td> | ||||
|     </tr> | ||||
|     <tr | ||||
|  | @ -67,20 +68,19 @@ exports[`Render should render table 1`] = ` | |||
|           } | ||||
|         } | ||||
|       > | ||||
|         <button | ||||
|           className="btn btn-secondary btn-small" | ||||
|         <Button | ||||
|           onClick={[Function]} | ||||
|           size="sm" | ||||
|           variant="secondary" | ||||
|         > | ||||
|           Update | ||||
|         </button> | ||||
|         <button | ||||
|           className="btn btn-danger btn-small" | ||||
|         </Button> | ||||
|         <Button | ||||
|           icon="trash-alt" | ||||
|           onClick={[Function]} | ||||
|         > | ||||
|           <Icon | ||||
|             name="trash-alt" | ||||
|           /> | ||||
|         </button> | ||||
|           size="sm" | ||||
|           variant="destructive" | ||||
|         /> | ||||
|       </td> | ||||
|     </tr> | ||||
|   </tbody> | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ import { getNavModel } from 'app/core/selectors/navModel'; | |||
| // Types
 | ||||
| import { StoreState } from 'app/types/'; | ||||
| import { DataSourceSettings } from '@grafana/data'; | ||||
| import { Alert, InfoBox } from '@grafana/ui'; | ||||
| import { Alert, Button, InfoBox, LinkButton } from '@grafana/ui'; | ||||
| import { getDataSourceLoadingNav } from '../state/navModel'; | ||||
| import PluginStateinfo from 'app/features/plugins/PluginStateInfo'; | ||||
| import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers'; | ||||
|  | @ -170,13 +170,13 @@ export class DataSourceSettingsPage extends PureComponent<Props> { | |||
|           <div> | ||||
|             <div className="gf-form-button-row"> | ||||
|               {showDelete && ( | ||||
|                 <button type="submit" className="btn btn-danger" onClick={this.onDelete}> | ||||
|                 <Button type="submit" variant="destructive" onClick={this.onDelete}> | ||||
|                   Delete | ||||
|                 </button> | ||||
|                 </Button> | ||||
|               )} | ||||
|               <a className="btn btn-inverse" href="datasources"> | ||||
|               <LinkButton variant="link" href="datasources"> | ||||
|                 Back | ||||
|               </a> | ||||
|               </LinkButton> | ||||
|             </div> | ||||
|           </div> | ||||
|         </Page.Contents> | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; | |||
| import { css, cx } from '@emotion/css'; | ||||
| import tinycolor from 'tinycolor2'; | ||||
| 
 | ||||
| import { LogMessageAnsi, Themeable, withTheme, getLogRowStyles, Icon } from '@grafana/ui'; | ||||
| import { LogMessageAnsi, Themeable, withTheme, getLogRowStyles, Icon, Button } from '@grafana/ui'; | ||||
| import { GrafanaTheme, LogRowModel, TimeZone, dateTimeFormat } from '@grafana/data'; | ||||
| 
 | ||||
| import { ElapsedTime } from './ElapsedTime'; | ||||
|  | @ -142,16 +142,16 @@ class LiveLogs extends PureComponent<Props, State> { | |||
|             /> | ||||
|           </tbody> | ||||
|         </table> | ||||
|         <div className={cx([styles.logsRowsIndicator])}> | ||||
|           <button onClick={isPaused ? onResume : onPause} className={cx('btn btn-secondary', styles.button)}> | ||||
|         <div className={styles.logsRowsIndicator}> | ||||
|           <Button variant="secondary" onClick={isPaused ? onResume : onPause} className={styles.button}> | ||||
|             <Icon name={isPaused ? 'play' : 'pause'} /> | ||||
|               | ||||
|             {isPaused ? 'Resume' : 'Pause'} | ||||
|           </button> | ||||
|           <button onClick={this.props.stopLive} className={cx('btn btn-inverse', styles.button)}> | ||||
|           </Button> | ||||
|           <Button variant="secondary" onClick={this.props.stopLive} className={styles.button}> | ||||
|             <Icon name="square-shape" size="lg" type="mono" /> | ||||
|               Exit live mode | ||||
|           </button> | ||||
|           </Button> | ||||
|           {isPaused || ( | ||||
|             <span> | ||||
|               Last line received: <ElapsedTime resetKey={this.props.logRows} humanize={true} /> ago | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import React, { PureComponent } from 'react'; | ||||
| import { connect, ConnectedProps } from 'react-redux'; | ||||
| import Page from 'app/core/components/Page/Page'; | ||||
| import { Tooltip, Icon } from '@grafana/ui'; | ||||
| import { Tooltip, Icon, Button } from '@grafana/ui'; | ||||
| import { SlideDown } from 'app/core/components/Animations/SlideDown'; | ||||
| import { getNavModel } from 'app/core/selectors/navModel'; | ||||
| import { StoreState } from 'app/types'; | ||||
|  | @ -105,9 +105,9 @@ export class FolderPermissions extends PureComponent<Props, State> { | |||
|               <Icon className="icon--has-hover page-sub-heading-icon" name="question-circle" /> | ||||
|             </Tooltip> | ||||
|             <div className="page-action-bar__spacer" /> | ||||
|             <button className="btn btn-primary pull-right" onClick={this.onOpenAddPermissions} disabled={isAdding}> | ||||
|             <Button className="pull-right" onClick={this.onOpenAddPermissions} disabled={isAdding}> | ||||
|               Add Permission | ||||
|             </button> | ||||
|             </Button> | ||||
|           </div> | ||||
|           <SlideDown in={isAdding}> | ||||
|             <AddPermission onAddPermission={this.onAddPermission} onCancel={this.onCancelAddPermission} /> | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import React, { PureComponent } from 'react'; | ||||
| import { connect, ConnectedProps } from 'react-redux'; | ||||
| import { LegacyForms } from '@grafana/ui'; | ||||
| import { Button, LegacyForms } from '@grafana/ui'; | ||||
| const { Input } = LegacyForms; | ||||
| import Page from 'app/core/components/Page/Page'; | ||||
| import appEvents from 'app/core/app_events'; | ||||
|  | @ -99,12 +99,12 @@ export class FolderSettingsPage extends PureComponent<Props, State> { | |||
|                 /> | ||||
|               </div> | ||||
|               <div className="gf-form-button-row"> | ||||
|                 <button type="submit" className="btn btn-primary" disabled={!folder.canSave || !folder.hasChanged}> | ||||
|                 <Button type="submit" disabled={!folder.canSave || !folder.hasChanged}> | ||||
|                   Save | ||||
|                 </button> | ||||
|                 <button className="btn btn-danger" onClick={this.onDelete} disabled={!folder.canSave}> | ||||
|                 </Button> | ||||
|                 <Button variant="destructive" onClick={this.onDelete} disabled={!folder.canSave}> | ||||
|                   Delete | ||||
|                 </button> | ||||
|                 </Button> | ||||
|               </div> | ||||
|             </form> | ||||
|           </div> | ||||
|  |  | |||
|  | @ -37,20 +37,19 @@ exports[`Render should enable save button 1`] = ` | |||
|         <div | ||||
|           className="gf-form-button-row" | ||||
|         > | ||||
|           <button | ||||
|             className="btn btn-primary" | ||||
|           <Button | ||||
|             disabled={false} | ||||
|             type="submit" | ||||
|           > | ||||
|             Save | ||||
|           </button> | ||||
|           <button | ||||
|             className="btn btn-danger" | ||||
|           </Button> | ||||
|           <Button | ||||
|             disabled={false} | ||||
|             onClick={[Function]} | ||||
|             variant="destructive" | ||||
|           > | ||||
|             Delete | ||||
|           </button> | ||||
|           </Button> | ||||
|         </div> | ||||
|       </form> | ||||
|     </div> | ||||
|  | @ -95,20 +94,19 @@ exports[`Render should render component 1`] = ` | |||
|         <div | ||||
|           className="gf-form-button-row" | ||||
|         > | ||||
|           <button | ||||
|             className="btn btn-primary" | ||||
|           <Button | ||||
|             disabled={true} | ||||
|             type="submit" | ||||
|           > | ||||
|             Save | ||||
|           </button> | ||||
|           <button | ||||
|             className="btn btn-danger" | ||||
|           </Button> | ||||
|           <Button | ||||
|             disabled={false} | ||||
|             onClick={[Function]} | ||||
|             variant="destructive" | ||||
|           > | ||||
|             Delete | ||||
|           </button> | ||||
|           </Button> | ||||
|         </div> | ||||
|       </form> | ||||
|     </div> | ||||
|  |  | |||
|  | @ -44,7 +44,9 @@ export class UserOrganizations extends PureComponent<Props> { | |||
|                         <td>{org.role}</td> | ||||
|                         <td className="text-right"> | ||||
|                           {org.orgId === user.orgId ? ( | ||||
|                             <span className="btn btn-primary btn-small">Current</span> | ||||
|                             <Button variant="secondary" size="sm" disabled> | ||||
|                               Current | ||||
|                             </Button> | ||||
|                           ) : ( | ||||
|                             <Button | ||||
|                               variant="secondary" | ||||
|  |  | |||
|  | @ -2,13 +2,14 @@ import React, { PureComponent } from 'react'; | |||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { SlideDown } from 'app/core/components/Animations/SlideDown'; | ||||
| import { LegacyForms, Tooltip, Icon } from '@grafana/ui'; | ||||
| import { LegacyForms, Tooltip, Icon, Button } from '@grafana/ui'; | ||||
| const { Input } = LegacyForms; | ||||
| 
 | ||||
| import { TeamGroup } from '../../types'; | ||||
| import { addTeamGroup, loadTeamGroups, removeTeamGroup } from './state/actions'; | ||||
| import { getTeamGroups } from './state/selectors'; | ||||
| import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; | ||||
| import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; | ||||
| 
 | ||||
| export interface Props { | ||||
|   groups: TeamGroup[]; | ||||
|  | @ -65,9 +66,9 @@ export class TeamGroupSync extends PureComponent<Props, State> { | |||
|       <tr key={group.groupId}> | ||||
|         <td>{group.groupId}</td> | ||||
|         <td style={{ width: '1%' }}> | ||||
|           <a className="btn btn-danger btn-small" onClick={() => this.onRemoveGroup(group)}> | ||||
|             <Icon name="times" style={{ marginBottom: 0 }} /> | ||||
|           </a> | ||||
|           <Button size="sm" variant="destructive" onClick={() => this.onRemoveGroup(group)}> | ||||
|             <Icon name="times" /> | ||||
|           </Button> | ||||
|         </td> | ||||
|       </tr> | ||||
|     ); | ||||
|  | @ -86,17 +87,15 @@ export class TeamGroupSync extends PureComponent<Props, State> { | |||
|           </Tooltip> | ||||
|           <div className="page-action-bar__spacer" /> | ||||
|           {groups.length > 0 && ( | ||||
|             <button className="btn btn-primary pull-right" onClick={this.onToggleAdding}> | ||||
|             <Button className="pull-right" onClick={this.onToggleAdding}> | ||||
|               <Icon name="plus" /> Add group | ||||
|             </button> | ||||
|             </Button> | ||||
|           )} | ||||
|         </div> | ||||
| 
 | ||||
|         <SlideDown in={isAdding}> | ||||
|           <div className="cta-form"> | ||||
|             <button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}> | ||||
|               <Icon name="times" /> | ||||
|             </button> | ||||
|             <CloseButton onClick={this.onToggleAdding} /> | ||||
|             <h5>Add External Group</h5> | ||||
|             <form className="gf-form-inline" onSubmit={this.onAddGroup}> | ||||
|               <div className="gf-form"> | ||||
|  | @ -110,9 +109,9 @@ export class TeamGroupSync extends PureComponent<Props, State> { | |||
|               </div> | ||||
| 
 | ||||
|               <div className="gf-form"> | ||||
|                 <button className="btn btn-primary gf-form-btn" type="submit" disabled={!this.isNewGroupValid()}> | ||||
|                 <Button type="submit" disabled={!this.isNewGroupValid()}> | ||||
|                   Add group | ||||
|                 </button> | ||||
|                 </Button> | ||||
|               </div> | ||||
|             </form> | ||||
|           </div> | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import React, { PureComponent } from 'react'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { Icon } from '@grafana/ui'; | ||||
| import { SlideDown } from 'app/core/components/Animations/SlideDown'; | ||||
| import { UserPicker } from 'app/core/components/Select/UserPicker'; | ||||
| import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; | ||||
|  | @ -13,6 +12,8 @@ import { config } from 'app/core/config'; | |||
| import { contextSrv, User as SignedInUser } from 'app/core/services/context_srv'; | ||||
| import TeamMemberRow from './TeamMemberRow'; | ||||
| import { setSearchMemberQuery } from './state/reducers'; | ||||
| import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; | ||||
| import { Button } from '@grafana/ui'; | ||||
| 
 | ||||
| export interface Props { | ||||
|   members: TeamMember[]; | ||||
|  | @ -79,28 +80,21 @@ export class TeamMembers extends PureComponent<Props, State> { | |||
|           </div> | ||||
| 
 | ||||
|           <div className="page-action-bar__spacer" /> | ||||
| 
 | ||||
|           <button | ||||
|             className="btn btn-primary pull-right" | ||||
|             onClick={this.onToggleAdding} | ||||
|             disabled={isAdding || !isTeamAdmin} | ||||
|           > | ||||
|           <Button className="pull-right" onClick={this.onToggleAdding} disabled={isAdding || !isTeamAdmin}> | ||||
|             Add member | ||||
|           </button> | ||||
|           </Button> | ||||
|         </div> | ||||
| 
 | ||||
|         <SlideDown in={isAdding}> | ||||
|           <div className="cta-form"> | ||||
|             <button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}> | ||||
|               <Icon name="times" /> | ||||
|             </button> | ||||
|             <CloseButton onClick={this.onToggleAdding} /> | ||||
|             <h5>Add team member</h5> | ||||
|             <div className="gf-form-inline"> | ||||
|               <UserPicker onSelected={this.onUserSelected} className="min-width-30" /> | ||||
|               {this.state.newTeamMember && ( | ||||
|                 <button className="btn btn-primary gf-form-btn" type="submit" onClick={this.onAddUserToTeam}> | ||||
|                 <Button type="submit" onClick={this.onAddUserToTeam}> | ||||
|                   Add to team | ||||
|                 </button> | ||||
|                 </Button> | ||||
|               )} | ||||
|             </div> | ||||
|           </div> | ||||
|  |  | |||
|  | @ -29,14 +29,9 @@ exports[`Render should render component 1`] = ` | |||
|     <div | ||||
|       className="cta-form" | ||||
|     > | ||||
|       <button | ||||
|         className="cta-form__close btn btn-transparent" | ||||
|       <CloseButton | ||||
|         onClick={[Function]} | ||||
|       > | ||||
|         <Icon | ||||
|           name="times" | ||||
|         /> | ||||
|       </button> | ||||
|       /> | ||||
|       <h5> | ||||
|         Add External Group | ||||
|       </h5> | ||||
|  | @ -58,13 +53,12 @@ exports[`Render should render component 1`] = ` | |||
|         <div | ||||
|           className="gf-form" | ||||
|         > | ||||
|           <button | ||||
|             className="btn btn-primary gf-form-btn" | ||||
|           <Button | ||||
|             disabled={true} | ||||
|             type="submit" | ||||
|           > | ||||
|             Add group | ||||
|           </button> | ||||
|           </Button> | ||||
|         </div> | ||||
|       </form> | ||||
|     </div> | ||||
|  | @ -104,15 +98,15 @@ exports[`Render should render groups table 1`] = ` | |||
|     <div | ||||
|       className="page-action-bar__spacer" | ||||
|     /> | ||||
|     <button | ||||
|       className="btn btn-primary pull-right" | ||||
|     <Button | ||||
|       className="pull-right" | ||||
|       onClick={[Function]} | ||||
|     > | ||||
|       <Icon | ||||
|         name="plus" | ||||
|       /> | ||||
|        Add group | ||||
|     </button> | ||||
|     </Button> | ||||
|   </div> | ||||
|   <SlideDown | ||||
|     in={false} | ||||
|  | @ -120,14 +114,9 @@ exports[`Render should render groups table 1`] = ` | |||
|     <div | ||||
|       className="cta-form" | ||||
|     > | ||||
|       <button | ||||
|         className="cta-form__close btn btn-transparent" | ||||
|       <CloseButton | ||||
|         onClick={[Function]} | ||||
|       > | ||||
|         <Icon | ||||
|           name="times" | ||||
|         /> | ||||
|       </button> | ||||
|       /> | ||||
|       <h5> | ||||
|         Add External Group | ||||
|       </h5> | ||||
|  | @ -149,13 +138,12 @@ exports[`Render should render groups table 1`] = ` | |||
|         <div | ||||
|           className="gf-form" | ||||
|         > | ||||
|           <button | ||||
|             className="btn btn-primary gf-form-btn" | ||||
|           <Button | ||||
|             disabled={true} | ||||
|             type="submit" | ||||
|           > | ||||
|             Add group | ||||
|           </button> | ||||
|           </Button> | ||||
|         </div> | ||||
|       </form> | ||||
|     </div> | ||||
|  | @ -194,19 +182,15 @@ exports[`Render should render groups table 1`] = ` | |||
|               } | ||||
|             } | ||||
|           > | ||||
|             <a | ||||
|               className="btn btn-danger btn-small" | ||||
|             <Button | ||||
|               onClick={[Function]} | ||||
|               size="sm" | ||||
|               variant="destructive" | ||||
|             > | ||||
|               <Icon | ||||
|                 name="times" | ||||
|                 style={ | ||||
|                   Object { | ||||
|                     "marginBottom": 0, | ||||
|                   } | ||||
|                 } | ||||
|               /> | ||||
|             </a> | ||||
|             </Button> | ||||
|           </td> | ||||
|         </tr> | ||||
|         <tr | ||||
|  | @ -222,19 +206,15 @@ exports[`Render should render groups table 1`] = ` | |||
|               } | ||||
|             } | ||||
|           > | ||||
|             <a | ||||
|               className="btn btn-danger btn-small" | ||||
|             <Button | ||||
|               onClick={[Function]} | ||||
|               size="sm" | ||||
|               variant="destructive" | ||||
|             > | ||||
|               <Icon | ||||
|                 name="times" | ||||
|                 style={ | ||||
|                   Object { | ||||
|                     "marginBottom": 0, | ||||
|                   } | ||||
|                 } | ||||
|               /> | ||||
|             </a> | ||||
|             </Button> | ||||
|           </td> | ||||
|         </tr> | ||||
|         <tr | ||||
|  | @ -250,19 +230,15 @@ exports[`Render should render groups table 1`] = ` | |||
|               } | ||||
|             } | ||||
|           > | ||||
|             <a | ||||
|               className="btn btn-danger btn-small" | ||||
|             <Button | ||||
|               onClick={[Function]} | ||||
|               size="sm" | ||||
|               variant="destructive" | ||||
|             > | ||||
|               <Icon | ||||
|                 name="times" | ||||
|                 style={ | ||||
|                   Object { | ||||
|                     "marginBottom": 0, | ||||
|                   } | ||||
|                 } | ||||
|               /> | ||||
|             </a> | ||||
|             </Button> | ||||
|           </td> | ||||
|         </tr> | ||||
|       </tbody> | ||||
|  |  | |||
|  | @ -17,13 +17,13 @@ exports[`Render should render component 1`] = ` | |||
|     <div | ||||
|       className="page-action-bar__spacer" | ||||
|     /> | ||||
|     <button | ||||
|       className="btn btn-primary pull-right" | ||||
|     <Button | ||||
|       className="pull-right" | ||||
|       disabled={false} | ||||
|       onClick={[Function]} | ||||
|     > | ||||
|       Add member | ||||
|     </button> | ||||
|     </Button> | ||||
|   </div> | ||||
|   <SlideDown | ||||
|     in={false} | ||||
|  | @ -31,14 +31,9 @@ exports[`Render should render component 1`] = ` | |||
|     <div | ||||
|       className="cta-form" | ||||
|     > | ||||
|       <button | ||||
|         className="cta-form__close btn btn-transparent" | ||||
|       <CloseButton | ||||
|         onClick={[Function]} | ||||
|       > | ||||
|         <Icon | ||||
|           name="times" | ||||
|         /> | ||||
|       </button> | ||||
|       /> | ||||
|       <h5> | ||||
|         Add team member | ||||
|       </h5> | ||||
|  | @ -109,13 +104,13 @@ exports[`Render should render team members 1`] = ` | |||
|     <div | ||||
|       className="page-action-bar__spacer" | ||||
|     /> | ||||
|     <button | ||||
|       className="btn btn-primary pull-right" | ||||
|     <Button | ||||
|       className="pull-right" | ||||
|       disabled={false} | ||||
|       onClick={[Function]} | ||||
|     > | ||||
|       Add member | ||||
|     </button> | ||||
|     </Button> | ||||
|   </div> | ||||
|   <SlideDown | ||||
|     in={false} | ||||
|  | @ -123,14 +118,9 @@ exports[`Render should render team members 1`] = ` | |||
|     <div | ||||
|       className="cta-form" | ||||
|     > | ||||
|       <button | ||||
|         className="cta-form__close btn btn-transparent" | ||||
|       <CloseButton | ||||
|         onClick={[Function]} | ||||
|       > | ||||
|         <Icon | ||||
|           name="times" | ||||
|         /> | ||||
|       </button> | ||||
|       /> | ||||
|       <h5> | ||||
|         Add team member | ||||
|       </h5> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import React, { MouseEvent, PureComponent } from 'react'; | ||||
| import { Icon } from '@grafana/ui'; | ||||
| import { Icon, LinkButton } from '@grafana/ui'; | ||||
| import { selectors } from '@grafana/e2e-selectors'; | ||||
| 
 | ||||
| import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types'; | ||||
|  | @ -98,14 +98,13 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> { | |||
|           {this.props.variables.length > 0 && variableToEdit === null && ( | ||||
|             <> | ||||
|               <VariablesDependenciesButton variables={this.props.variables} /> | ||||
|               <a | ||||
|               <LinkButton | ||||
|                 type="button" | ||||
|                 className="btn btn-primary" | ||||
|                 onClick={this.onNewVariable} | ||||
|                 aria-label={selectors.pages.Dashboard.Settings.Variables.List.newButton} | ||||
|               > | ||||
|                 New | ||||
|               </a> | ||||
|               </LinkButton> | ||||
|             </> | ||||
|           )} | ||||
|         </div> | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| // Libraries
 | ||||
| import React, { PureComponent } from 'react'; | ||||
| import { LinkButton } from '@grafana/ui'; | ||||
| 
 | ||||
| // Types
 | ||||
| import { PluginConfigPageProps, DataSourcePluginMeta, DataSourceJsonData } from '@grafana/data'; | ||||
|  | @ -17,14 +18,14 @@ export class TestInfoTab extends PureComponent<Props> { | |||
|         See github for more information about setting up a reproducible test environment. | ||||
|         <br /> | ||||
|         <br /> | ||||
|         <a | ||||
|           className="btn btn-inverse" | ||||
|         <LinkButton | ||||
|           variant="secondary" | ||||
|           href="https://github.com/grafana/grafana/tree/master/devenv" | ||||
|           target="_blank" | ||||
|           rel="noopener noreferrer" | ||||
|         > | ||||
|           GitHub | ||||
|         </a> | ||||
|         </LinkButton> | ||||
|         <br /> | ||||
|       </div> | ||||
|     ); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue