[Canvas] i18n work on workpad header (and a few header CTAs) and convert to typescript (#44943) (#45405)

* i18n work on workpad header and a few header ctas

* Convert WorkpadHeader to typescript

* String ordering cleanup

* Addressing some feedback

* Adding state

* lint

* Shortcut type refactor

* Revert "Shortcut type refactor"

This reverts commit d00e48853bcb16fdb14f9bca8b2536c920e8d650.

* Using new State type

* Removing unused type

* Updating state type
This commit is contained in:
Poff Poffenberger 2019-09-12 10:19:07 -05:00 committed by GitHub
parent 81a3dad6c2
commit 69878a5a86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 218 additions and 46 deletions

View file

@ -12,5 +12,113 @@ export const ComponentStrings = {
i18n.translate('xpack.canvas.embedObject.noMatchingObjectsMessage', {
defaultMessage: 'No matching objects found.',
}),
getTitleText: () =>
i18n.translate('xpack.canvas.embedObject.titleText', {
defaultMessage: 'Embed Object',
}),
},
Asset: {
getCopyAssetTooltipText: () =>
i18n.translate('xpack.canvas.asset.copyAssetTooltipText', {
defaultMessage: 'Copy id to clipboard',
}),
getCreateImageTooltipText: () =>
i18n.translate('xpack.canvas.asset.createImageTooltipText', {
defaultMessage: 'Create image element',
}),
getDeleteAssetTooltipText: () =>
i18n.translate('xpack.canvas.asset.deleteAssetTooltipText', {
defaultMessage: 'Delete',
}),
getDownloadAssetTooltipText: () =>
i18n.translate('xpack.canvas.asset.downloadAssetTooltipText', {
defaultMessage: 'Download',
}),
getThumbnailAltText: () =>
i18n.translate('xpack.canvas.asset.thumbnailAltText', {
defaultMessage: 'Asset thumbnail',
}),
},
AssetManager: {
getBtnText: () =>
i18n.translate('xpack.canvas.assetManager.buttonText', {
defaultMessage: 'Manage assets',
}),
getConfirmModalBtnText: () =>
i18n.translate('xpack.canvas.assetManager.confirmModalButtonText', {
defaultMessage: 'Remove',
}),
getConfirmModalMessageText: () =>
i18n.translate('xpack.canvas.assetManager.confirmModalMessage', {
defaultMessage: 'Are you sure you want to remove this asset?',
}),
getConfirmModalTitleText: () =>
i18n.translate('xpack.canvas.assetManager.confirmModalTitleText', {
defaultMessage: 'Remove Asset',
}),
},
AssetModal: {
getDescriptionText: () =>
i18n.translate('xpack.canvas.assetModal.descriptionText', {
defaultMessage:
'Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets.',
}),
getEmptyAssetsMessageText: () =>
i18n.translate('xpack.canvas.assetModal.emptyAssetsMessage', {
defaultMessage: 'Import your assets to get started',
}),
getFilePickerPromptText: () =>
i18n.translate('xpack.canvas.assetModal.filePickerPromptText', {
defaultMessage: 'Select or drag and drop images',
}),
getLoadingText: () =>
i18n.translate('xpack.canvas.assetModal.loadingText', {
defaultMessage: 'Uploading images',
}),
getModalCloseBtnText: () =>
i18n.translate('xpack.canvas.assetModal.modalCloseButtonText', {
defaultMessage: 'Close',
}),
getModalTitleText: () =>
i18n.translate('xpack.canvas.assetModal.modalTitleText', {
defaultMessage: 'Manage workpad assets',
}),
getSpaceUsedText: (percentageUsed: number) =>
i18n.translate('xpack.canvas.assetModal.spacedUsedText', {
defaultMessage: '{percentageUsed}% space used',
values: {
percentageUsed,
},
}),
},
WorkpadHeader: {
getAddElementBtnText: () =>
i18n.translate('xpack.canvas.workpadHeader.addElementButtonText', {
defaultMessage: 'Add element',
}),
getAddElementModalCloseBtnText: () =>
i18n.translate('xpack.canvas.workpadHeader.addElementModalCloseButtonText', {
defaultMessage: 'Close',
}),
getEmbedObjectBtnText: () =>
i18n.translate('xpack.canvas.workpadHeader.embedObjectButtonText', {
defaultMessage: 'Embed object',
}),
getFullScreenTooltipText: () =>
i18n.translate('xpack.canvas.workpadHeader.fullscreenTooltipText', {
defaultMessage: 'Enter fullscreen mode',
}),
getHideEditControlText: () =>
i18n.translate('xpack.canvas.workpadHeader.hideEditControlText', {
defaultMessage: 'Hide editing controls',
}),
getNoWritePermText: () =>
i18n.translate('xpack.canvas.workpadHeader.noWritePermissionText', {
defaultMessage: "You don't have permission to edit this workpad",
}),
getShowEditControlText: () =>
i18n.translate('xpack.canvas.workpadHeader.showEditControlText', {
defaultMessage: 'Show editing controls',
}),
},
};

View file

@ -16,10 +16,15 @@ import {
EuiToolTip,
} from '@elastic/eui';
import React, { FunctionComponent } from 'react';
import { ComponentStrings } from '../../../i18n';
import { Clipboard } from '../clipboard';
import { Download } from '../download';
import { AssetType } from '../../../types';
const { Asset: strings } = ComponentStrings;
interface Props {
/** The asset to be rendered */
asset: AssetType;
@ -36,10 +41,10 @@ export const Asset: FunctionComponent<Props> = props => {
const createImage = (
<EuiFlexItem className="asset-create-image" grow={false}>
<EuiToolTip content="Create image element">
<EuiToolTip content={strings.getCreateImageTooltipText()}>
<EuiButtonIcon
iconType="vector"
aria-label="Create image element"
aria-label={strings.getCreateImageTooltipText()}
onClick={() => onCreate(asset)}
/>
</EuiToolTip>
@ -48,9 +53,9 @@ export const Asset: FunctionComponent<Props> = props => {
const downloadAsset = (
<EuiFlexItem className="asset-download" grow={false}>
<EuiToolTip content="Download">
<EuiToolTip content={strings.getDownloadAssetTooltipText()}>
<Download fileName={asset.id} content={asset.value}>
<EuiButtonIcon iconType="sortDown" aria-label="Download" />
<EuiButtonIcon iconType="sortDown" aria-label={strings.getDownloadAssetTooltipText()} />
</Download>
</EuiToolTip>
</EuiFlexItem>
@ -58,9 +63,9 @@ export const Asset: FunctionComponent<Props> = props => {
const copyAsset = (
<EuiFlexItem grow={false}>
<EuiToolTip content="Copy id to clipboard">
<EuiToolTip content={strings.getCopyAssetTooltipText()}>
<Clipboard content={asset.id} onCopy={(result: boolean) => result && onCopy(asset)}>
<EuiButtonIcon iconType="copyClipboard" aria-label="Copy id to clipboard" />
<EuiButtonIcon iconType="copyClipboard" aria-label={strings.getCopyAssetTooltipText()} />
</Clipboard>
</EuiToolTip>
</EuiFlexItem>
@ -68,11 +73,11 @@ export const Asset: FunctionComponent<Props> = props => {
const deleteAsset = (
<EuiFlexItem grow={false}>
<EuiToolTip content="Delete">
<EuiToolTip content={strings.getDeleteAssetTooltipText()}>
<EuiButtonIcon
color="danger"
iconType="trash"
aria-label="Delete"
aria-label={strings.getDeleteAssetTooltipText()}
onClick={() => onDelete(asset)}
/>
</EuiToolTip>
@ -86,7 +91,7 @@ export const Asset: FunctionComponent<Props> = props => {
size="original"
url={props.asset.value}
fullScreenIconColor="dark"
alt="Asset thumbnail"
alt={strings.getThumbnailAltText()}
style={{ backgroundImage: `url(${props.asset.value})` }}
/>
</div>

View file

@ -13,10 +13,15 @@ import {
} from '@elastic/eui';
import PropTypes from 'prop-types';
import React, { Fragment, PureComponent } from 'react';
import { ComponentStrings } from '../../../i18n';
import { ConfirmModal } from '../confirm_modal';
import { AssetType } from '../../../types';
import { AssetModal } from './asset_modal';
const { AssetManager: strings } = ComponentStrings;
interface Props {
/** A list of assets, if available */
assetValues: AssetType[];
@ -80,9 +85,9 @@ export class AssetManager extends PureComponent<Props, State> {
const confirmModal = (
<ConfirmModal
isOpen={this.state.deleteId !== null}
title="Remove Asset"
message="Are you sure you want to remove this asset?"
confirmButtonText="Remove"
title={strings.getConfirmModalTitleText()}
message={strings.getConfirmModalMessageText()}
confirmButtonText={strings.getConfirmModalBtnText()}
onConfirm={this.doDelete}
onCancel={this.resetDelete}
/>
@ -90,7 +95,7 @@ export class AssetManager extends PureComponent<Props, State> {
return (
<Fragment>
<EuiButtonEmpty onClick={this.showModal}>Manage assets</EuiButtonEmpty>
<EuiButtonEmpty onClick={this.showModal}>{strings.getBtnText()}</EuiButtonEmpty>
{isModalVisible ? assetModal : null}
{confirmModal}
</Fragment>

View file

@ -24,12 +24,17 @@ import {
} from '@elastic/eui';
import PropTypes from 'prop-types';
import React, { FunctionComponent } from 'react';
import { ComponentStrings } from '../../../i18n';
// @ts-ignore
import { ASSET_MAX_SIZE } from '../../../common/lib/constants';
import { Loading } from '../loading';
import { Asset } from './asset';
import { AssetType } from '../../../types';
const { AssetModal: strings } = ComponentStrings;
interface Props {
/** The assets to display within the modal */
assetValues: AssetType[];
@ -68,7 +73,7 @@ export const AssetModal: FunctionComponent<Props> = props => {
<EuiPanel className="canvasAssetManager__emptyPanel">
<EuiEmptyPrompt
iconType="importAction"
title={<h2>Import your assets to get started</h2>}
title={<h2>{strings.getEmptyAssetsMessageText()}</h2>}
titleSize="xs"
/>
</EuiPanel>
@ -83,15 +88,15 @@ export const AssetModal: FunctionComponent<Props> = props => {
>
<EuiModalHeader className="canvasAssetManager__modalHeader">
<EuiModalHeaderTitle className="canvasAssetManager__modalHeaderTitle">
Manage workpad assets
{strings.getModalTitleText()}
</EuiModalHeaderTitle>
<EuiFlexGroup className="canvasAssetManager__fileUploadWrapper">
<EuiFlexItem grow={false}>
{isLoading ? (
<Loading animated text="Uploading images" />
<Loading animated text={strings.getLoadingText()} />
) : (
<EuiFilePicker
initialPromptText="Select or drag and drop images"
initialPromptText={strings.getFilePickerPromptText()}
compressed
multiple
onChange={onFileUpload}
@ -103,10 +108,7 @@ export const AssetModal: FunctionComponent<Props> = props => {
</EuiModalHeader>
<EuiModalBody>
<EuiText size="s" color="subdued">
<p>
Below are the image assets in this workpad. Any assets that are currently in use
cannot be determined at this time. To reclaim space, delete assets.
</p>
<p>{strings.getDescriptionText()}</p>
</EuiText>
<EuiSpacer />
{assetValues.length ? (
@ -137,11 +139,13 @@ export const AssetModal: FunctionComponent<Props> = props => {
/>
</EuiFlexItem>
<EuiFlexItem grow={false} className="eui-textNoWrap">
<EuiText id="CanvasAssetManagerLabel">{percentageUsed}% space used</EuiText>
<EuiText id="CanvasAssetManagerLabel">
{strings.getSpaceUsedText(percentageUsed)}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiButton size="s" onClick={onClose}>
Close
{strings.getModalCloseBtnText()}
</EuiButton>
</EuiModalFooter>
</EuiModal>

View file

@ -6,7 +6,6 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
SavedObjectFinder,
SavedObjectMetaData,
@ -58,9 +57,7 @@ export class AddEmbeddableFlyout extends React.Component<Props> {
<EuiFlyout ownFocus onClose={this.props.onClose} data-test-subj="dashboardAddPanel">
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<h2>
<FormattedMessage id="xpack.canvas.embedObject.title" defaultMessage="Embed Object" />
</h2>
<h2>{strings.getTitleText()}</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>

View file

@ -104,7 +104,7 @@ export class EmbeddableFlyoutPortal extends React.Component<Props> {
}
}
export const AddEmbeddablePanel = compose<Props, {}>(
export const AddEmbeddablePanel = compose<Props, { onClose: () => void }>(
connect(
mapStateToProps,
mapDispatchToProps,

View file

@ -5,22 +5,41 @@
*/
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
// @ts-ignore untyped local
import { canUserWrite } from '../../state/selectors/app';
// @ts-ignore untyped local
import { getSelectedPage, isWriteable } from '../../state/selectors/workpad';
// @ts-ignore untyped local
import { setWriteable } from '../../state/actions/workpad';
import { WorkpadHeader as Component } from './workpad_header';
import { State } from '../../../types';
import { WorkpadHeader as Component, Props as ComponentProps } from './workpad_header';
const mapStateToProps = state => ({
interface StateProps {
isWriteable: boolean;
canUserWrite: boolean;
selectedPage: string;
}
interface DispatchProps {
setWriteable: (isWorkpadWriteable: boolean) => void;
}
const mapStateToProps = (state: State): StateProps => ({
isWriteable: isWriteable(state) && canUserWrite(state),
canUserWrite: canUserWrite(state),
selectedPage: getSelectedPage(state),
});
const mapDispatchToProps = dispatch => ({
setWriteable: isWriteable => dispatch(setWriteable(isWriteable)),
const mapDispatchToProps = (dispatch: Dispatch) => ({
setWriteable: (isWorkpadWriteable: boolean) => dispatch(setWriteable(isWorkpadWriteable)),
});
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
const mergeProps = (
stateProps: StateProps,
dispatchProps: DispatchProps,
ownProps: ComponentProps
): ComponentProps => ({
...stateProps,
...dispatchProps,
...ownProps,

View file

@ -43,7 +43,7 @@ interface Props {
enabled: boolean;
}
export const WorkpadExport = compose<ComponentProps, Props>(
export const WorkpadExport = compose<ComponentProps, {}>(
connect(mapStateToProps),
withProps(
({ workpad, pageCount, enabled }: Props): ComponentProps => ({

View file

@ -6,6 +6,7 @@
import React from 'react';
import PropTypes from 'prop-types';
// @ts-ignore no @types definition
import { Shortcuts } from 'react-shortcuts';
import {
EuiFlexItem,
@ -18,17 +19,39 @@ import {
EuiModalFooter,
EuiToolTip,
} from '@elastic/eui';
import { ComponentStrings } from '../../../i18n';
// @ts-ignore untyped local
import { AssetManager } from '../asset_manager';
// @ts-ignore untyped local
import { ElementTypes } from '../element_types';
import { ToolTipShortcut } from '../tool_tip_shortcut/';
import { AddEmbeddablePanel } from '../embeddable_flyout';
// @ts-ignore untyped local
import { ControlSettings } from './control_settings';
// @ts-ignore untyped local
import { RefreshControl } from './refresh_control';
// @ts-ignore untyped local
import { FullscreenControl } from './fullscreen_control';
import { WorkpadExport } from './workpad_export';
import { WorkpadZoom } from './workpad_zoom';
export class WorkpadHeader extends React.PureComponent {
const { WorkpadHeader: strings } = ComponentStrings;
export interface Props {
isWriteable: boolean;
toggleWriteable: () => void;
canUserWrite: boolean;
selectedPage: string;
}
interface State {
isModalVisible: boolean;
isPanelVisible: boolean;
}
export class WorkpadHeader extends React.PureComponent<Props, State> {
static propTypes = {
isWriteable: PropTypes.bool,
toggleWriteable: PropTypes.func,
@ -36,12 +59,13 @@ export class WorkpadHeader extends React.PureComponent {
state = { isModalVisible: false, isPanelVisible: false };
_fullscreenButton = ({ toggleFullscreen }) => (
_fullscreenButton = ({ toggleFullscreen }: { toggleFullscreen: () => void }) => (
<EuiToolTip
position="bottom"
content={
<span>
Enter fullscreen mode <ToolTipShortcut namespace="PRESENTATION" action="FULLSCREEN" />
{strings.getFullScreenTooltipText()}{' '}
<ToolTipShortcut namespace="PRESENTATION" action="FULLSCREEN" />
</span>
}
>
@ -53,7 +77,7 @@ export class WorkpadHeader extends React.PureComponent {
</EuiToolTip>
);
_keyHandler = action => {
_keyHandler = (action: string) => {
if (action === 'EDITING') {
this.props.toggleWriteable();
}
@ -76,7 +100,7 @@ export class WorkpadHeader extends React.PureComponent {
<ElementTypes onClose={this._hideElementModal} />
<EuiModalFooter>
<EuiButton size="s" onClick={this._hideElementModal}>
Close
{strings.getAddElementModalCloseBtnText()}
</EuiButton>
</EuiModalFooter>
</EuiModal>
@ -85,12 +109,20 @@ export class WorkpadHeader extends React.PureComponent {
_embeddableAdd = () => <AddEmbeddablePanel onClose={this._hideEmbeddablePanel} />;
_getEditToggleToolTip = ({ textOnly } = { textOnly: false }) => {
_getEditToggleToolTipText = () => {
if (!this.props.canUserWrite) {
return "You don't have permission to edit this workpad";
return strings.getNoWritePermText();
}
const content = this.props.isWriteable ? `Hide editing controls` : `Show editing controls`;
const content = this.props.isWriteable
? strings.getHideEditControlText()
: strings.getShowEditControlText();
return content;
};
_getEditToggleToolTip = ({ textOnly } = { textOnly: false }) => {
const content = this._getEditToggleToolTipText();
if (textOnly) {
return content;
@ -149,7 +181,7 @@ export class WorkpadHeader extends React.PureComponent {
iconType={isWriteable ? 'lockOpen' : 'lock'}
onClick={toggleWriteable}
size="s"
aria-label={this._getEditToggleToolTip({ textOnly: true })}
aria-label={this._getEditToggleToolTipText()}
isDisabled={!canUserWrite}
/>
</EuiToolTip>
@ -163,7 +195,9 @@ export class WorkpadHeader extends React.PureComponent {
<AssetManager />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={this._showEmbeddablePanel}>Embed object</EuiButtonEmpty>
<EuiButtonEmpty onClick={this._showEmbeddablePanel}>
{strings.getEmbedObjectBtnText()}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
@ -173,7 +207,7 @@ export class WorkpadHeader extends React.PureComponent {
data-test-subj="add-element-button"
onClick={this._showElementModal}
>
Add element
{strings.getAddElementBtnText()}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -32,7 +32,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
setZoomScale: (scale: number) => dispatch(setZoomScale(scale)),
});
export const WorkpadZoom = compose<ComponentProps, void>(
export const WorkpadZoom = compose<ComponentProps, {}>(
connect(
mapStateToProps,
mapDispatchToProps