Skip to content

Commit

Permalink
Read Only Mode in Canvas (#23304)
Browse files Browse the repository at this point in the history
* Added readOnly to transient state

* Added check that user has permission to edit when loading workpads

* Shows toolbar in readOnly mode

* Disables page controls in read only mode

* Disables add page button when editing disabled

* Added comment to workpad routes

* Cleaned up page manager styles

* Added a comment to es_persist

* Renamed transient.readOnly to transient.readOnlyUser

* Replaced isEditing in transient state with readOnly in workpad state

* Cleaned up workpad_header

* Fixed logic in workpad_header

* Disables workpad controls instead of hiding them and adds tooltips

* Switched eye icon to lock icon in workpad header

* Renamed readOnly and readOnlyUser variables and functions

* Added dispatch to update canUserWrite in create/clone/delete workpads

* Fixed typo

* Removed unnecessary prop from workpad

* Refactored tooltips in workpad_loader

* Added workpad selector tests
  • Loading branch information
cqliu1 authored Oct 22, 2018
1 parent 744edd9 commit 9d424f9
Show file tree
Hide file tree
Showing 31 changed files with 241 additions and 136 deletions.
12 changes: 12 additions & 0 deletions x-pack/plugins/canvas/public/apps/workpad/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { setWorkpad } from '../../state/actions/workpad';
import { setAssets, resetAssets } from '../../state/actions/assets';
import { gotoPage } from '../../state/actions/pages';
import { getWorkpad } from '../../state/selectors/workpad';
import { setCanUserWrite } from '../../state/actions/transient';
import { WorkpadApp } from './workpad_app';

export const routes = [
Expand All @@ -29,6 +30,10 @@ export const routes = [
router.redirectTo('loadWorkpad', { id: newWorkpad.id, page: 1 });
} catch (err) {
notify.error(err, { title: `Couldn't create workpad` });
// TODO: remove this and switch to checking user privileges when canvas loads when granular app privileges are introduced
// https://github.com/elastic/kibana/issues/20277
if (err.response.status === 403) dispatch(setCanUserWrite(false));
router.redirectTo('home');
}
},
meta: {
Expand All @@ -48,6 +53,13 @@ export const routes = [
const { assets, ...workpad } = fetchedWorkpad;
dispatch(setWorkpad(workpad));
dispatch(setAssets(assets));

// tests if user has permissions to write to workpads
// TODO: remove this and switch to checking user privileges when canvas loads when granular app privileges are introduced
// https://github.com/elastic/kibana/issues/20277
workpadService.update(params.id, fetchedWorkpad).catch(err => {
if (err.response.status === 403) dispatch(setCanUserWrite(false));
});
} catch (err) {
notify.error(err, { title: `Couldn't load workpad with ID` });
return router.redirectTo('home');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import { connect } from 'react-redux';
import { compose, branch, renderComponent } from 'recompose';
import { initializeWorkpad } from '../../../state/actions/workpad';
import { selectElement } from '../../../state/actions/transient';
import { getEditing, getAppReady } from '../../../state/selectors/app';
import { getWorkpad } from '../../../state/selectors/workpad';
import { canUserWrite, getAppReady } from '../../../state/selectors/app';
import { getWorkpad, isWriteable } from '../../../state/selectors/workpad';
import { LoadWorkpad } from './load_workpad';
import { WorkpadApp as Component } from './workpad_app';

const mapStateToProps = state => {
const appReady = getAppReady(state);

return {
editing: getEditing(state),
isWriteable: isWriteable(state) && canUserWrite(state),
appReady: typeof appReady === 'object' ? appReady : { ready: appReady },
workpad: getWorkpad(state),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { WorkpadHeader } from '../../../components/workpad_header';

export class WorkpadApp extends React.PureComponent {
static propTypes = {
editing: PropTypes.bool,
isWriteable: PropTypes.bool.isRequired,
deselectElement: PropTypes.func,
initializeWorkpad: PropTypes.func.isRequired,
};
Expand All @@ -23,7 +23,7 @@ export class WorkpadApp extends React.PureComponent {
}

render() {
const { editing, deselectElement } = this.props;
const { isWriteable, deselectElement } = this.props;

return (
<div className="canvasLayout">
Expand All @@ -42,18 +42,16 @@ export class WorkpadApp extends React.PureComponent {
</div>
</div>

{editing && (
{isWriteable && (
<div className="canvasLayout__sidebar hide-for-sharing">
<Sidebar />
</div>
)}
</div>

{editing ? (
<div className="canvasLayout__footer hide-for-sharing">
<Toolbar />
</div>
) : null}
<div className="canvasLayout__footer hide-for-sharing">
<Toolbar />
</div>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@

import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getEditing } from '../../state/selectors/app';
import { getResolvedArgs, getSelectedPage } from '../../state/selectors/workpad';
import { getResolvedArgs, getSelectedPage, isWriteable } from '../../state/selectors/workpad';
import { getState, getValue, getError } from '../../lib/resolved_arg';
import { ElementWrapper as Component } from './element_wrapper';
import { createHandlers as createHandlersWithDispatch } from './lib/handlers';

const mapStateToProps = (state, { element }) => ({
isEditing: getEditing(state),
isWriteable: isWriteable(state),
resolvedArg: getResolvedArgs(state, element.id, 'expressionRenderable'),
selectedPage: getSelectedPage(state),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
import { connect } from 'react-redux';
import { compose, withState } from 'recompose';
import * as pageActions from '../../state/actions/pages';
import { getSelectedPage, getWorkpad, getPages } from '../../state/selectors/workpad';
import { canUserWrite } from '../../state/selectors/app';
import { getSelectedPage, getWorkpad, getPages, isWriteable } from '../../state/selectors/workpad';
import { PageManager as Component } from './page_manager';

const mapStateToProps = state => ({
isWriteable: isWriteable(state) && canUserWrite(state),
pages: getPages(state),
selectedPage: getSelectedPage(state),
workpadId: getWorkpad(state).id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { PagePreview } from '../page_preview';

export class PageManager extends React.PureComponent {
static propTypes = {
isWriteable: PropTypes.bool.isRequired,
pages: PropTypes.array.isRequired,
workpadId: PropTypes.string.isRequired,
addPage: PropTypes.func.isRequired,
Expand Down Expand Up @@ -102,11 +103,11 @@ export class PageManager extends React.PureComponent {
};

renderPage = (page, i) => {
const { selectedPage, workpadId, movePage, duplicatePage } = this.props;
const { isWriteable, selectedPage, workpadId, movePage, duplicatePage } = this.props;
const pageNumber = i + 1;

return (
<Draggable key={page.id} draggableId={page.id} index={i}>
<Draggable key={page.id} draggableId={page.id} index={i} isDragDisabled={!isWriteable}>
{provided => (
<div
key={page.id}
Expand All @@ -133,6 +134,7 @@ export class PageManager extends React.PureComponent {
aria-label={`Load page number ${pageNumber}`}
>
<PagePreview
isWriteable={isWriteable}
page={page}
height={100}
pageNumber={pageNumber}
Expand All @@ -151,7 +153,7 @@ export class PageManager extends React.PureComponent {
};

render() {
const { pages, addPage, deleteId } = this.props;
const { pages, addPage, deleteId, isWriteable } = this.props;
const { showTrayPop } = this.state;

return (
Expand All @@ -178,17 +180,19 @@ export class PageManager extends React.PureComponent {
</Droppable>
</DragDropContext>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip
anchorClassName="canvasPageManager__addPageTip"
content="Add a new page to this workpad"
position="left"
>
<button onClick={addPage} className="canvasPageManager__addPage">
<EuiIcon color="ghost" type="plusInCircle" size="l" />
</button>
</EuiToolTip>
</EuiFlexItem>
{isWriteable && (
<EuiFlexItem grow={false}>
<EuiToolTip
anchorClassName="canvasPageManager__addPageTip"
content="Add a new page to this workpad"
position="left"
>
<button onClick={addPage} className="canvasPageManager__addPage">
<EuiIcon color="ghost" type="plusInCircle" size="l" />
</button>
</EuiToolTip>
</EuiFlexItem>
)}
</EuiFlexGroup>
<ConfirmModal
isOpen={deleteId != null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,9 @@
position: relative;
}

.canvasPageManager__pageList {
@include euiScrollBar;
display: flex;
overflow-x: auto;
overflow-y: hidden;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
.canvasPageManager__pages,
.canvasPageManager__addPage {
height: 144px;
}

.canvasPageManager--trayPop > div {
Expand All @@ -26,14 +19,25 @@
}
}

.canvasPageManager__pageList {
@include euiScrollBar;
display: flex;
overflow-x: auto;
overflow-y: hidden;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}

.canvasPageManager__addPage {
width: $euiSizeXXL + $euiSize;
background: $euiColorSecondary;
color: $euiColorGhost;
opacity: 0;
animation: buttonPop $euiAnimSpeedNormal $euiAnimSlightResistance;
animation-fill-mode: forwards;
height: 144px;
}

.canvasPageManager__addPageTip {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,32 @@ import PropTypes from 'prop-types';
import { DomPreview } from '../dom_preview';
import { PageControls } from './page_controls';

export const PagePreview = ({ page, pageNumber, height, duplicatePage, confirmDelete }) => (
export const PagePreview = ({
isWriteable,
page,
pageNumber,
height,
duplicatePage,
confirmDelete,
}) => (
<div
className="canvasPageManager__pagePreview"
style={{ backgroundColor: page.style.background }}
>
<DomPreview elementId={page.id} pageNumber={pageNumber} height={height} />
<PageControls
pageId={page.id}
pageNumber={pageNumber}
onDuplicate={duplicatePage}
onDelete={confirmDelete}
/>
{isWriteable && (
<PageControls
pageId={page.id}
pageNumber={pageNumber}
onDuplicate={duplicatePage}
onDelete={confirmDelete}
/>
)}
</div>
);

PagePreview.propTypes = {
isWriteable: PropTypes.bool.isRequired,
page: PropTypes.shape({
id: PropTypes.string.isRequired,
style: PropTypes.shape({
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/canvas/public/components/toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { compose, withState, getContext, withHandlers } from 'recompose';
import { getEditing } from '../../state/selectors/app';

import {
getWorkpad,
Expand All @@ -19,7 +18,6 @@ import {
import { Toolbar as Component } from './toolbar';

const mapStateToProps = state => ({
editing: getEditing(state),
workpadName: getWorkpadName(state),
workpadId: getWorkpad(state).id,
totalPages: getWorkpad(state).pages.length,
Expand Down
4 changes: 1 addition & 3 deletions x-pack/plugins/canvas/public/components/toolbar/toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import { Tray } from './tray';

export const Toolbar = props => {
const {
editing,
selectedElement,
tray,
setTray,
Expand Down Expand Up @@ -63,7 +62,7 @@ export const Toolbar = props => {
expression: !elementIsSelected ? null : <Expression done={done} />,
};

return !editing ? null : (
return (
<div className="canvasToolbar hide-for-sharing">
{trays[tray] && <Tray done={done}>{trays[tray]}</Tray>}
<Navbar>
Expand Down Expand Up @@ -122,7 +121,6 @@ export const Toolbar = props => {

Toolbar.propTypes = {
workpadName: PropTypes.string,
editing: PropTypes.bool,
tray: PropTypes.node,
setTray: PropTypes.func.isRequired,
nextPage: PropTypes.func.isRequired,
Expand Down
3 changes: 1 addition & 2 deletions x-pack/plugins/canvas/public/components/workpad/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { compose, withState, withProps, getContext, withHandlers } from 'recompo
import { transitionsRegistry } from '../../lib/transitions_registry';
import { undoHistory, redoHistory } from '../../state/actions/history';
import { fetchAllRenderables } from '../../state/actions/elements';
import { getFullscreen, getEditing } from '../../state/selectors/app';
import { getFullscreen } from '../../state/selectors/app';
import {
getSelectedPageIndex,
getAllElements,
Expand All @@ -25,7 +25,6 @@ const mapStateToProps = state => ({
totalElementCount: getAllElements(state).length,
workpad: getWorkpad(state),
isFullscreen: getFullscreen(state),
isEditing: getEditing(state),
});

const mapDispatchToProps = {
Expand Down
13 changes: 7 additions & 6 deletions x-pack/plugins/canvas/public/components/workpad_header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,23 @@

import { compose, withState } from 'recompose';
import { connect } from 'react-redux';
import { getEditing } from '../../state/selectors/app';
import { getWorkpadName, getSelectedPage } from '../../state/selectors/workpad';
import { setEditing } from '../../state/actions/transient';
import { canUserWrite } from '../../state/selectors/app';
import { getWorkpadName, getSelectedPage, isWriteable } from '../../state/selectors/workpad';
import { setWriteable } from '../../state/actions/workpad';
import { getAssets } from '../../state/selectors/assets';
import { addElement } from '../../state/actions/elements';
import { WorkpadHeader as Component } from './workpad_header';

const mapStateToProps = state => ({
editing: getEditing(state),
isWriteable: isWriteable(state) && canUserWrite(state),
canUserWrite: canUserWrite(state),
workpadName: getWorkpadName(state),
selectedPage: getSelectedPage(state),
hasAssets: Object.keys(getAssets(state)).length ? true : false,
});

const mapDispatchToProps = dispatch => ({
setEditing: editing => dispatch(setEditing(editing)),
setWriteable: isWriteable => dispatch(setWriteable(isWriteable)),
addElement: pageId => partialElement => dispatch(addElement(pageId, partialElement)),
});

Expand All @@ -30,7 +31,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => ({
...dispatchProps,
...ownProps,
addElement: dispatchProps.addElement(stateProps.selectedPage),
toggleEditing: () => dispatchProps.setEditing(!stateProps.editing),
toggleWriteable: () => dispatchProps.setWriteable(!stateProps.isWriteable),
});

export const WorkpadHeader = compose(
Expand Down
Loading

0 comments on commit 9d424f9

Please sign in to comment.