Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify styling of widget action icons #8064

Merged
merged 9 commits into from
Jun 4, 2020
Merged
60 changes: 60 additions & 0 deletions graylog2-web-interface/src/components/common/IconButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// @flow strict
import * as React from 'react';
import PropTypes from 'prop-types';
import styled, { type StyledComponent } from 'styled-components';

import { type ThemeInterface } from 'theme';
import Icon from 'components/common/Icon';

const Wrapper: StyledComponent<{}, ThemeInterface, HTMLButtonElement> = styled.button(({ theme }) => `
display: inline-flex;
justify-content: center;
align-items: center;

height: 25px;
width: 25px;
border: 0

background-color: transparent;
cursor: pointer;
color: ${theme.color.gray[70]};
font-size: 16px;

:hover {
background-color: ${theme.color.gray[90]};
}

:active {
background-color: ${theme.color.gray[80]};
}
`);


type Props = {
title: string,
onClick?: () => void,
};

const handleClick = (onClick) => {
if (typeof onClick === 'function') {
onClick();
}
};

const IconButton = ({ title, onClick, ...rest }: Props) => (
<Wrapper title={title} onClick={() => handleClick(onClick)}>
<Icon {...rest} />
</Wrapper>
);

IconButton.propTypes = {
title: PropTypes.string,
onClick: PropTypes.func,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered requiring the onClick prop, but e.g. in ReplaySearchButton we need to wrap the button with a link, because we can't open the page in another tab with history.push()

};

IconButton.defaultProps = {
onClick: undefined,
title: undefined,
};

export default IconButton;
1 change: 1 addition & 0 deletions graylog2-web-interface/src/components/common/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { default as ExpandableListItem } from './ExpandableListItem';
export { default as ExternalLink } from './ExternalLink';
export { default as ExternalLinkButton } from './ExternalLinkButton';
export { default as Icon } from './Icon';
export { default as IconButton } from './IconButton';
export { default as IfPermitted } from './IfPermitted';
export { default as InteractableModal } from './InteractableModal';
export { default as ISODurationInput } from './ISODurationInput';
Expand Down
2 changes: 1 addition & 1 deletion graylog2-web-interface/src/components/errors/ErrorPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import styled, { createGlobalStyle } from 'styled-components';
import NotFoundBackgroundImage from 'assets/not-found-bg.jpg';

import AppContentGrid from 'components/layout/AppContentGrid';
import { DocumentTitle } from 'components/common';
import DocumentTitle from 'components/common/DocumentTitle';
import ErrorJumbotron from 'components/errors/ErrorJumbotron';

const GlobalStyle = createGlobalStyle`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,11 @@ import * as React from 'react';
import { useContext } from 'react';
import styled, { type StyledComponent } from 'styled-components';

import { Icon } from 'components/common';
import { IconButton } from 'components/common';
import SearchLink from 'components/search/SearchLink';
import { createElasticsearchQueryString } from 'views/logic/queries/Query';
import DrilldownContext from '../contexts/DrilldownContext';

const DitheredIcon: StyledComponent<{}, {}, HTMLElement> = styled(Icon)`
opacity: 0.3;
position: relative;
top: 3px;
`;

const NeutralLink: StyledComponent<{}, {}, HTMLAnchorElement> = styled.a`
color: inherit;
text-decoration: none;
Expand All @@ -35,11 +29,9 @@ const ReplaySearchButton = () => {
const searchLink = buildSearchLink(timerange, query.query_string, streams);
return (
<NeutralLink href={searchLink} target="_blank" rel="noopener noreferrer" title="Replay search">
<DitheredIcon name="play" />
<IconButton name="play" />
</NeutralLink>
);
};

ReplaySearchButton.propTypes = {};

export default ReplaySearchButton;
16 changes: 0 additions & 16 deletions graylog2-web-interface/src/views/components/widgets/Widget.css

This file was deleted.

65 changes: 38 additions & 27 deletions graylog2-web-interface/src/views/components/widgets/Widget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react';
import * as Immutable from 'immutable';
import PropTypes from 'prop-types';
import { browserHistory } from 'react-router';
import styled from 'styled-components';

import Routes from 'routing/Routes';
import { MenuItem } from 'components/graylog';
Expand Down Expand Up @@ -50,6 +51,16 @@ import WidgetErrorBoundary from './WidgetErrorBoundary';
import IfDashboard from '../dashboard/IfDashboard';
import ReplaySearchButton from './ReplaySearchButton';

const WidgetActionsWBar = styled.div`
> * {
margin-right: 5px;

:last-child {
margin-right: 0;
}
}
`;

type Props = {
id: string,
view: ViewStoreState,
Expand Down Expand Up @@ -302,33 +313,33 @@ class Widget extends React.Component<Props, State> {
loading={loading}
onRename={(newTitle) => TitlesActions.set('widget', id, newTitle)}
editing={editing}>
<IfInteractive>
<IfDashboard>
<ReplaySearchButton />
{' '}
</IfDashboard>
<WidgetHorizontalStretch widgetId={widget.id}
widgetType={widget.type}
onStretch={onPositionsChange}
position={position} />
{' '}
<WidgetActionDropdown>
<MenuItem onSelect={this._onToggleEdit}>Edit</MenuItem>
<MenuItem onSelect={() => this._onDuplicate(id)}>Duplicate</MenuItem>
{type === MessagesWidget.type && <MenuItem onSelect={() => this._onToggleCSVExport()}>Export to CSV</MenuItem>}
<IfSearch>
<MenuItem onSelect={this._onToggleCopyToDashboard}>Copy to Dashboard</MenuItem>
</IfSearch>
<MenuItem divider />
<MenuItem onSelect={() => this._onDelete(widget)}>Delete</MenuItem>
</WidgetActionDropdown>
{showCopyToDashboard && (
<CopyToDashboard widgetId={id}
onSubmit={this._onCopyToDashboard}
onCancel={this._onToggleCopyToDashboard} />
)}
{showCsvExport && <CSVExportModal view={view.view} directExportWidgetId={widget.id} closeModal={this._onToggleCSVExport} />}
</IfInteractive>
<WidgetActionsWBar>
<IfInteractive>
<IfDashboard>
<ReplaySearchButton />
</IfDashboard>
<WidgetHorizontalStretch widgetId={widget.id}
widgetType={widget.type}
onStretch={onPositionsChange}
position={position} />
<WidgetActionDropdown>
<MenuItem onSelect={this._onToggleEdit}>Edit</MenuItem>
<MenuItem onSelect={() => this._onDuplicate(id)}>Duplicate</MenuItem>
{type === MessagesWidget.type && <MenuItem onSelect={() => this._onToggleCSVExport()}>Export to CSV</MenuItem>}
<IfSearch>
<MenuItem onSelect={this._onToggleCopyToDashboard}>Copy to Dashboard</MenuItem>
</IfSearch>
<MenuItem divider />
<MenuItem onSelect={() => this._onDelete(widget)}>Delete</MenuItem>
</WidgetActionDropdown>
{showCopyToDashboard && (
<CopyToDashboard widgetId={id}
onSubmit={this._onCopyToDashboard}
onCancel={this._onToggleCopyToDashboard} />
)}
{showCsvExport && <CSVExportModal view={view.view} directExportWidgetId={widget.id} closeModal={this._onToggleCSVExport} />}
</IfInteractive>
</WidgetActionsWBar>
</WidgetHeader>
)}
</InteractiveContext.Consumer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
import * as React from 'react';

import ActionDropdown from 'views/components/common/ActionDropdown';
import { Icon } from 'components/common';
import { IconButton } from 'components/common';

import styles from './Widget.css';

type Props = {
children: React.Node,
};

const WidgetActionDropdown = ({ children }: Props) => {
const widgetActionDropdownCaret = <Icon data-testid="widgetActionDropDown" name="chevron-down" className={`${styles.widgetActionDropdownCaret} ${styles.tonedDown}`} />;
const widgetActionDropdownCaret = <IconButton data-testid="widgetActionDropDown" name="chevron-down" title="Open actions dropdown" />;
return (
<ActionDropdown element={widgetActionDropdownCaret}>
{children}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';

import { widgetDefinition } from 'views/logic/Widgets';
import { Icon } from 'components/common';
import style from './WidgetHorizontalStretch.css';
import { IconButton } from 'components/common';

class WidgetHorizontalStretch extends React.Component {
static propTypes = {
Expand All @@ -25,11 +24,11 @@ class WidgetHorizontalStretch extends React.Component {
render() {
const { position } = this.props;
const { width } = position;
const icon = width === Infinity ? 'compress' : 'arrows-h';
const stretched = width === Infinity;
const icon = stretched ? 'compress' : 'arrows-h';
const title = stretched ? 'Compress width' : 'Stretch width';
return (
<span>
<Icon role="link" tabIndex={0} onClick={this._onClick} name={icon} className={style.button} />
</span>
<IconButton tabIndex={0} onClick={this._onClick} name={icon} title={title} />
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,37 @@ exports[`<Widget /> should render with empty props 1`] = `
loading="false"
title="Widget Title"
>
<span>
<svg
class="svg-inline--fa fa-arrows-alt-h"
/>
</span>

<span
role="presentation"
<div
class="Widget__WidgetActionsWBar-sc-1k4o2f7-0 ckMMeL"
>
<button
class="IconButton__Wrapper-sc-1q956yw-0 kZTuI"
title="Stretch width"
>
<svg
class="svg-inline--fa fa-arrows-alt-h"
/>
</button>
<span
role="presentation"
>
<span>
<svg
class="svg-inline--fa fa-chevron-down"
data-testid="widgetActionDropDown"
/>
<span
role="presentation"
>
<span>
<button
class="IconButton__Wrapper-sc-1q956yw-0 kZTuI"
title="Open actions dropdown"
>
<svg
class="svg-inline--fa fa-chevron-down"
data-testid="widgetActionDropDown"
/>
</button>
</span>
</span>
</span>
</span>
</div>
</widget-header>
<div
class="spinnerContainer"
Expand Down