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

EZP-28841: Content On the Fly v2 #55

Merged
merged 1 commit into from
Mar 12, 2018
Merged

Conversation

dew326
Copy link
Member

@dew326 dew326 commented Mar 2, 2018

Jira ticket: https://jira.ez.no/browse/EZP-28841
Requires: ezsystems/ezplatform-admin-ui#366

TODO

  • content on the fly in UDW
  • forced language
  • check permission - missing endpoint

@dew326 dew326 self-assigned this Mar 2, 2018
@dew326 dew326 changed the title [WIP] EZP-28841: Content On the Fly v2 EZP-28841: Content On the Fly v2 Mar 7, 2018
@dew326 dew326 requested a review from sunpietro March 7, 2018 07:50
Copy link
Contributor

@sunpietro sunpietro left a comment

Choose a reason for hiding this comment

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

There's quite a lof of things to change.

import './css/content.creator.component.css';

export default class ContentCreatorComponent extends Component {
constructor(props) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If you're not using props to initialize state, then you can omit the props variable in the constructor method.

Copy link
Member Author

Choose a reason for hiding this comment

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

You can but I don't think you should.
Class components should always call the base constructor with props. from React doc: https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class

Copy link
Contributor

Choose a reason for hiding this comment

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

This is recommendation, right. We can follow this rule. It's especially useful when you're extending your own component classes. In this case, when you extend base component class it's not that relevant. Because any kind of props won't affect the way the base class works.

Copy link
Member Author

Choose a reason for hiding this comment

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

I know it will not cause any problems when extending React.Component for now, but if they have this in their doc they may require this at some point in the future.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, let's keep it then. From the currect perspective, it brings no value now, but it's a recommended by React doc.

handleIframeLoad() {
const locationId = this._iframe.contentWindow.document.querySelector('meta[name="LocationID"]');

if (this._iframe.contentWindow.location.pathname !== this.generateIframeUrl() && !locationId) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this.generateIframeUrl() be cached?

} else {
this.setState(state => Object.assign({}, state, { iframeLoading: false }));

this._iframe.contentWindow.onbeforeunload = () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: I would put the iframe event handlers to the second callback of this.setState method. Do you think it's a bad approach? When moving the event handlers assignment to the callback you apply them right the state is updated (in a sync way).

Copy link
Contributor

Choose a reason for hiding this comment

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

The question is still valid.

Copy link
Member Author

Choose a reason for hiding this comment

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

Those onbeforeunload and onunload is not related to the setState, I think callback for setState should be used when its depends on the state.

}

generateIframeUrl() {
return window.Routing.generate('ezplatform.content.create.on_the_fly', {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it should be initialized in the constructor method, because you're not updating app state in case of props updates. This function will always return the same output.
In the constructor it should be stored liked this:

this.iframeUrl = window.Routing.generate(...);

* @returns {Element}
* @memberof FinderTreeLeafComponent
*/
renderLoadingIcon() {
Copy link
Contributor

Choose a reason for hiding this comment

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

renderLoadingSpinner

<div {...wrapperAttrs}>
<TabContentPanelComponent {...props}>
<div className="c-create-panel__first-step">
<div className="c-create-panel__step-title">1) {props.labels.contentOnTheFly.chooseLangaugeAndContentType}</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

Using ol > li isn't the right way to go?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm?

Copy link
Member Author

Choose a reason for hiding this comment

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

So li would need to have inside a whole create or finder component? I think it's not the best structure.

languageCode,
locationId
}, callback) => {
const endpoint = window.Routing.generate('ezplatform.content.create.on_the_fly.has_access', {
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no need to duplicate the names: window.Routing.generate('ezplatform.content.create.on_the_fly.has_access', { languageCode, contentTypeIdentifier, locationId })

const request = new Request(endpoint, {
method: 'GET',
headers: {'X-CSRF-Token': token},
mode: 'cors',
Copy link
Contributor

Choose a reason for hiding this comment

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

We decided to switch to proper mode: same-origin

this.onContentTypeSelected = this.onContentTypeSelected.bind(this);
this.handlePublish = this.handlePublish.bind(this);

if (props.forcedLanguageCOTF) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe it should be renamed to something like: cotfForcedLanguage or maybe it can be done differently. Because, here youre hardcoding a kind-of-dependency to the COTF which might not exist on a specific instalation.
Do you really need this information here, in UDW module?

Copy link
Member Author

Choose a reason for hiding this comment

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

COTF is part of a UDW module and the configuration must be set on the UDW and then pass to the COTF.
Because, here youre hardcoding a kind-of-dependency to the COTF which might not exist on a specific instalation. - that's why it has a default value '' which will prevent from breaking in customers instalations

this.setState(state => Object.assign({}, state, {isCheckingPermission: false}));

if (this.state.hasPermission !== response.access) {
this.setState(state => Object.assign({}, state, {hasPermission: response.access}));
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you should set state once, not twice in the same scope.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure how this could be in one. We want to change isCheckingPermission every time when the request is finished but change the permission state only when have to.

Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think about it?

this.setState(state => Object.assign({}, state, {
    isCheckingPermission: false,
    hasPermission: state.hasPermission !== response.access ? response.access : state.hasPermission
}));

Copy link
Member Author

Choose a reason for hiding this comment

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

I can try but as far as I know thanks to immutable with Object.assign this will make rerender of react component and this is what I wanted to avoid in this situation.

Copy link
Contributor

Choose a reason for hiding this comment

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

You're already doing it, so why doing it twice?

Copy link
Contributor

Choose a reason for hiding this comment

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

React will compare boolean values (I assume they are boolean). If there's any change then it will cause re-rendering of any related piece of code.

@dew326
Copy link
Member Author

dew326 commented Mar 12, 2018

ping @sunpietro

}

handlePublish() {
this._iframe.contentWindow.onbeforeunload = () => {};
Copy link
Contributor

Choose a reason for hiding this comment

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

With ReactJS we abandoned the rule of adding underscores into protected variables. These will not be exposed in the React code anyway.

} else {
this.setState(state => Object.assign({}, state, { iframeLoading: false }));

this._iframe.contentWindow.onbeforeunload = () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

The question is still valid.

.replace('{contentType}', selectedContentType.name)
.replace('{language}', selectedLanguage.name);
const iframeUrl = this.generateIframeUrl();
const contentClass = this.state.iframeLoading ? "m-ud__content is-loading" : "m-ud__content";
Copy link
Contributor

Choose a reason for hiding this comment

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

CS: Single quotes, please.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not: m-ud__content--is-loading?

}

updateFilterQuery(event) {
const value = event.target.value.toLowerCase();
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you thought about naming it as filterQuery?

}, 200);
}

renderItem(item, index) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The question is still valid.

background-color: #e1f5ff;
}


Copy link
Contributor

Choose a reason for hiding this comment

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

CS: Remove extra empty line here.

<div {...wrapperAttrs}>
<TabContentPanelComponent {...props}>
<div className="c-create-panel__first-step">
<div className="c-create-panel__step-title">1) {props.labels.contentOnTheFly.chooseLangaugeAndContentType}</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm?


fetch(request)
.then(handleRequestResponse)
.then(json => callback(json))
Copy link
Contributor

Choose a reason for hiding this comment

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

.then(callback)


fetch(request)
.then(handleRequestResponse)
.then(json => callback(json))
Copy link
Contributor

Choose a reason for hiding this comment

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

.then(callback)

if (this.props.onlyContentOnTheFly) {
return (
<div className="m-ud__panels">
{this.renderSinglePanel(createPanelConfig)}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm thinking about moving these configs into some kidn of array. But maybe in other PR.


window.clearTimeout(this._filterTimeout);

this._filterTimeout = window.setTimeout(() => {
this.setState(state => Object.assign({}, state, {filterQuery: value}));
this.setState(state => Object.assign({}, state, {filterQuery: filterQuery}));
Copy link
Contributor

Choose a reason for hiding this comment

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

{ filterQuery }

@@ -21,14 +21,14 @@ export default class ChooseLanguageComponent extends Component {

this.props.onLanguageSelected(selectedLanguage);

this.setState(state => Object.assign({}, state, {selected: selectedLanguage}));
this.setState(state => Object.assign({}, state, {selectedLanguage: selectedLanguage}));
Copy link
Contributor

Choose a reason for hiding this comment

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

{ selectedLanguage }

</div>
);
}
const CreateComponent = (props) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

You can drop import of React component in the first line: import React from 'react';

@lserwatka lserwatka merged commit b99d7e1 into ezsystems:master Mar 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants