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

Builder - Content Editor - Web page (C039 - #32) #4600

Merged
merged 10 commits into from Dec 10, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docma-template/less/bootstrap/variables.less
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@
//** Popover body background color
@popover-bg: #fff;
//** Popover maximum width
@popover-max-width: 276px;
@popover-max-width: 288px;
//** Popover border color
@popover-border-color: rgba(0,0,0,.2);
//** Popover fallback border color
Expand Down
2 changes: 1 addition & 1 deletion docma-template/less/bs-variables.less
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@
//** Popover body background color
@popover-bg: #fff;
//** Popover maximum width
@popover-max-width: 276px;
@popover-max-width: 288px;
//** Popover border color
@popover-border-color: rgba(0,0,0,.2);
//** Popover fallback border color
Expand Down
6 changes: 6 additions & 0 deletions web/client/actions/geostory.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const ADD = "GEOSTORY:ADD";
export const ADD_RESOURCE = "GEOSTORY:ADD_RESOURCE";
export const CHANGE_MODE = "GEOSTORY:CHANGE_MODE";
export const EDIT_RESOURCE = "GEOSTORY:EDIT_RESOURCE";
export const EDIT_WEBPAGE = "GEOSTORY:EDIT_WEBPAGE";
export const ERRORS_LOGO = "GEOSTORY:ERRORS_LOGO";
export const GEOSTORY_LOADED = "GEOSTORY:GEOSTORY_LOADED";
export const LOAD_GEOSTORY = "GEOSTORY:LOAD_GEOSTORY";
Expand All @@ -27,6 +28,7 @@ export const SELECT_CARD = "GEOSTORY:SELECT_CARD";
export const SET_CONTROL = "GEOSTORY:SET_CONTROL";
export const SET_RESOURCE = "GEOSTORY:SET_RESOURCE";
export const SET_CURRENT_STORY = "GEOSTORY:SET_CURRENT_STORY";
export const SET_WEBPAGE_URL = "GEOSTORY:SET_WEBPAGE_URL";
export const TOGGLE_CARD_PREVIEW = "GEOSTORY:TOGGLE_CARD_PREVIEW";
export const TOGGLE_SETTINGS_PANEL = "GEOSTORY:TOGGLE_SETTINGS_PANEL";
export const TOGGLE_SETTING = "GEOSTORY:TOGGLE_SETTING";
Expand Down Expand Up @@ -209,3 +211,7 @@ export const setFocusOnContent = (status, target, selector, hideContent, path) =
* @param {*} value value used to update the prop
*/
export const updateSetting = (prop, value) => ({type: UPDATE_SETTING, prop, value});

export const setWebPageUrl = (src) => ({ type: SET_WEBPAGE_URL, src });

export const editWebPage = ({ path }, owner = 'GEOSTORY') => ({ type: EDIT_WEBPAGE, path, owner });
4 changes: 3 additions & 1 deletion web/client/components/geostory/builder/SectionsPreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ const Icon = ({ type, src, thumbnail } = {}) => {
map: '1-map',
columnleft: 'align-left',
columnright: 'align-right',
columncenter: 'align-center'
columncenter: 'align-center',
webPage: 'code'
};
const imgSrc = src || thumbnail;
return imgSrc ? <img src={imgSrc}/> : <Glyphicon glyph={glyphs[type] || 'picture'} />;
Expand Down Expand Up @@ -139,6 +140,7 @@ const previewContents = {
const contentType = content.type === 'column'
? `${content.type}${content.align || 'center'}`
: content.type;
console.log(contentType, content.type);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
console.log(contentType, content.type);

const PreviewContents = previewContents[content.type];
return {
id: content.id,
Expand Down
9 changes: 8 additions & 1 deletion web/client/components/geostory/contents/Column.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default ({
mode,
add = () => {},
editMedia = () => {},
editWebPage = () => {},
update = () => {},
remove = () => {}
}) => (
Expand All @@ -39,6 +40,7 @@ export default ({
mode={mode}
add={add}
editMedia={editMedia}
editWebPage={editWebPage}
update={update}
remove={remove}
viewWidth={viewWidth}
Expand All @@ -47,7 +49,8 @@ export default ({
[ContentTypes.TEXT]: ['remove'],
[MediaTypes.IMAGE]: ['editMedia', 'size', 'align', 'remove'],
[MediaTypes.MAP]: ['editMedia', 'editMap', 'size', 'align', 'remove'],
[MediaTypes.VIDEO]: ['editMedia', 'remove'] // TODO change this list for video
[MediaTypes.VIDEO]: ['editMedia', 'remove'], // TODO change this list for video
[ContentTypes.WEBPAGE]: ['editURL', 'size', 'align', 'remove']
}}
addButtons={[{
glyph: 'sheet',
Expand All @@ -58,6 +61,10 @@ export default ({
glyph: 'picture',
tooltipId: 'geostory.addMediaContent',
template: ContentTypes.MEDIA
}, {
glyph: 'code',
tooltipId: 'geostory.addWebPageContent',
template: ContentTypes.WEBPAGE
}]}
/>
);
5 changes: 4 additions & 1 deletion web/client/components/geostory/contents/Content.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import React from 'react';
import Text from './Text';
import { ContentTypes, MediaTypes } from '../../../utils/GeoStoryUtils';
import {Media} from '../media';
import { Media } from '../media';
import WebPage from './WebPageWrapper';
const DummyComponent = ({ type }) => <div className="ms-content ms-content-unknown">{`warning: unknown content type "${type}"`}</div>;

/**
Expand All @@ -23,6 +24,8 @@ const getComponent = type => {
case MediaTypes.IMAGE:
case MediaTypes.MAP:
return Media;
case ContentTypes.WEBPAGE:
return WebPage;
default:
return DummyComponent;
}
Expand Down
10 changes: 10 additions & 0 deletions web/client/components/geostory/contents/ContentToolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ const toolButtons = {
onClick: () => {
update( 'editMap', !editMap);
}
}),
editURL: ({ editURL = false, path, editWebPage = () => {}}) => ({
glyph: "pencil",
visible: true,
disabled: editURL,
bsStyle: editURL ? "success" : "default",
tooltipId: "geostory.contentToolbar.editURL",
onClick: () => {
editWebPage({path});
}
})
};

Expand Down
2 changes: 2 additions & 0 deletions web/client/components/geostory/contents/Contents.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default ({
mode,
sectionType,
editMedia = () => {},
editWebPage = () => {},
add = () => {},
update = () => {},
remove = () => {}
Expand All @@ -61,6 +62,7 @@ export default ({
sectionType={sectionType}
viewHeight={viewHeight}
editMedia={({path = ""}, ...args) => editMedia({path: `contents[{"id": "${id}"}]` + path}, ...args)}
editWebPage={({path = ""}, ...args) => editWebPage({ path: `contents[{"id": "${id}"}]` + path }, ...args)}
// restructure the path to give it the correct scope
add={(path, ...args) => add(`contents[{"id": "${id}"}].` + path, ...args)}
update={(path, ...args) => update(`contents[{"id": "${id}"}].` + path, ...args)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default compose(
withHandlers({
// NOTE: adds the section initial path to content. The Contents adds contents[...] on it's own for inner add buttons
editMedia: ({ editMedia = () => { }, sectionId }) => ({path}, ...args) => editMedia({path: `sections[{"id": "${sectionId}"}].` + path}, ...args),
editWebPage: ({ editWebPage = () => { }, sectionId }) => ({path}, ...args) => editWebPage({path: `sections[{"id": "${sectionId}"}].` + path}, ...args),
add: ({ add = () => { }, sectionId }) => (path, ...args) => add(`sections[{"id": "${sectionId}"}].` + path, ...args),
update: ({ update = () => { }, sectionId }) => (path, ...args) => update(`sections[{"id": "${sectionId}"}].` + path, ...args),
remove: ({ remove = () => { }, sectionId }) => (path, ...args) => remove(`sections[{"id": "${sectionId}"}].` + path, ...args)
Expand Down
34 changes: 10 additions & 24 deletions web/client/components/geostory/contents/WebPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,19 @@
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import { compose, branch, withProps } from 'recompose';
import { connect } from "react-redux";
import { createSelector } from 'reselect';
import { resourcesSelector } from '../../../selectors/geostory';
import emptyState from '../../misc/enhancers/emptyState';
import { find } from 'lodash';
import { compose } from 'recompose';
import { getWebPageComponentHeight } from '../../../utils/GeoStoryUtils';
import { webPagePlaceholderEnhancer } from './enhancers/editURL';

const WebPage = ({ src, width, height }) => (
<div
className="ms-webpage-wrapper"
width={width}
height={height}
>
<iframe src={src} />
export const WebPage = ({ src, size, viewHeight }) => (
<div className="ms-webpage-wrapper">
<iframe
src={src}
height={`${getWebPageComponentHeight(size, viewHeight)}px`}
/>
</div>
);

export default compose(
branch(
({resourceId}) => resourceId,
compose(
connect(createSelector(resourcesSelector, (resources) => ({resources}))),
withProps(({resources, resourceId: id}) => (find(resources, { id }) || {}).data)
),
emptyState(
({src = "", width, height} = {}) => (!src || !width || !height),
() => ({ glyph: "code" })
)
)
webPagePlaceholderEnhancer
)(WebPage);
101 changes: 101 additions & 0 deletions web/client/components/geostory/contents/WebPageWrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2019, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import Dialog from '../../misc/StandardDialog';
import { FormControl, ControlLabel, FormGroup, Button } from 'react-bootstrap';
import { connect } from 'react-redux';
import { setWebPageUrl } from '../../../actions/geostory';
import Message from '../../I18N/Message';
import Portal from '../../misc/Portal';
import PropTypes from 'prop-types';
import WebPage from './WebPage';
import { compose, withHandlers } from 'recompose';
import { isValidURL } from '../../../utils/URLUtils';

export class WebPageWrapper extends React.PureComponent {
static propTypes = {
editURL: PropTypes.bool,
onClose: PropTypes.func,
onChange: PropTypes.func,
src: PropTypes.string,
id: PropTypes.string
}

state = {
url: this.props.src || '',
error: false
}

render() {
const { onClose, editURL } = this.props;
const { url, error } = this.state;
return (
<React.Fragment>
<Portal>
<Dialog
modal
show={editURL}
onClickOut={(e) => e.preventDefault() && e.stopPropagation()}
title={<Message msgId="geostory.webPageCreator.title" />}
onClose={onClose}
id="web-page-creator"
footer={(
<Button bsSize="small" onClick={this.save} >
<Message msgId="geostory.webPageCreator.saveButton" />
</Button>
)}
>
{ error && (
<div className="dropzone-errorBox alert-danger">
<Message msgId="geostory.webPageCreator.error"/>
</div>
)}
<FormGroup controlId="WEBPAGE_URL" validationState={error && 'error'}>
<ControlLabel>
<Message msgId="geostory.webPageCreator.url.label" />
</ControlLabel>
<FormControl
label="URL"
type="text"
value={url}
onChange={this.updateURL}
required
/>
</FormGroup>
</Dialog>
</Portal>
<WebPage {...this.props} />
</React.Fragment>
);
}

updateURL = ({ target: { value: url }}) => {
this.setState({ url });
}

save = () => {
const { url } = this.state;
const error = !isValidURL(url);
Copy link
Contributor

Choose a reason for hiding this comment

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

can you pass ConfigUtils.getConfigProp("GeoStoryValidIframeURLRegex") as second argument?
in that case we can override it anytime without changing the code

Verify that if the property is missing in config, then it uses the default one

Copy link
Author

Choose a reason for hiding this comment

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

ok

Copy link
Author

Choose a reason for hiding this comment

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

@MV88 done

this.setState({ error: !isValidURL(url) });
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
this.setState({ error: !isValidURL(url) });
this.setState({ error });

if (!error) {
this.props.onChange(url);
}
}
}

const wrappedComponent = compose(
withHandlers({
onClose: ({update = () => {}}) => () => update('editURL', false, 'merge')
})
)(WebPageWrapper);

export default connect(
null,
{ onChange: setWebPageUrl }
)(wrappedComponent);
20 changes: 10 additions & 10 deletions web/client/components/geostory/contents/__tests__/WebPage-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ describe('WebPage component', () => {
it('should render page when provided all neccesary data', () => {
ReactDOM.render(<WebPage
src="http://www.google.com"
height={100}
width={100}
size="large"
viewHeight={100}
/>, document.getElementById("container"));
const container = document.getElementById("container");
expect(container.querySelector(".ms-webpage-wrapper")).toExist();
Expand All @@ -34,37 +34,37 @@ describe('WebPage component', () => {
it('should render page when provided all neccesary data', () => {
ReactDOM.render(<WebPage
src="http://www.google.com"
height={100}
width={100}
size="large"
viewHeight={100}
/>, document.getElementById("container"));
const container = document.getElementById("container");
expect(container.querySelector(".ms-webpage-wrapper")).toExist();
});

it('should\'t render page when src is not provided', () => {
ReactDOM.render(<WebPage
height={100}
width={100}
size="large"
viewHeight={100}
/>, document.getElementById("container"));
const container = document.getElementById("container");
expect(container.querySelector(".ms-webpage-wrapper")).toBe(null);
expect(container.querySelector(".empty-state-main-view")).toExist();
});

it('should\'t render page when width is not provided', () => {
it('should\'t render page when size is not provided', () => {
ReactDOM.render(<WebPage
src="http://www.google.com"
height={100}
viewHeight={100}
/>, document.getElementById("container"));
const container = document.getElementById("container");
expect(container.querySelector(".ms-webpage-wrapper")).toBe(null);
expect(container.querySelector(".empty-state-main-view")).toExist();
});

it('should\'t render page when height is not provided', () => {
it('should\'t render page when viewHeight is not provided', () => {
ReactDOM.render(<WebPage
src="http://www.google.com"
width={100}
size="large"
/>, document.getElementById("container"));
const container = document.getElementById("container");
expect(container.querySelector(".ms-webpage-wrapper")).toBe(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2019, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import ReactDOM from 'react-dom';

import expect from 'expect';
import { WebPageWrapper } from '../WebPageWrapper';

describe('WebPageWrapper', () => {
beforeEach((done) => {
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});
afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});

it('render with defaults', () => {
ReactDOM.render(<WebPageWrapper />, document.getElementById("container"));
const el = document.querySelector('.modal-dialog');
expect(el).toBe(null);
});

it('should render when editURL is true', () => {
const props = { editURL: true };
ReactDOM.render(<WebPageWrapper {...props} />, document.getElementById("container"));
const el = document.querySelector('.modal-dialog');
expect(el).toExist();
});
});
Loading