Can not render custom form field in Liferay 7.2 - liferay

I was migrating a 7.1 DXP portal to 7.2 DXP but I can not bring my custom form field to work.
I used the dynamic-data-mapping-form-field-type module as a blueprint. My new field is available inside of the form builder - but when using it, nothing gets rendered. I don't have an errors on build, deploy or in JS console. Unfortunately, there is no blade example for 7.2 yet so that I could not start with a simple example.
My question is: how to hook in the field Soy template to render?

It is being implemented in https://issues.liferay.com/browse/LPS-98417 and it is true there is no blade example.
Meanwhile you can download following example code:
https://github.com/natocesarrego/slider

In case anyone is here for Liferay 7.3. Liferay 7.3 moved from soy templates to pure react components. You could use Liferay's module as a blueprint again.
import { FieldBase } from 'dynamic-data-mapping-form-fieldtype/FieldBase/ReactFieldBase.es'
import React, { useState, useEffect, useRef } from 'react';
const Text = ({ readOnly,
id,
name,
onBlur,
onChange,
onFocus,
placeholder,
value: initialValue }) => {
const [value, setValue] = useState(initialValue);
return (
<>
<input type="text" />
</>
);
};
const Main = (props) => {
return (
<FieldBase {...props}>
<Text {...props} />
</FieldBase>
);
}
export default Main;
In this case we are importing FieldBase component that is the Liferay field wrapper that will take care of adding any default Liferay behavior (validation, names, placeholder, tooltip etc...). We did the same when we used Soy templates.
You can create the module from form-field blade template. Then remove the soy template files along with the following line in package.json
"build-soy": "metalsoy --externalMsgFormat \"Liferay.Language.get(‘\\$2’)\" --soyDeps \"./node_modules/clay-*/src/**/*.soy\" \"./node_modules/com.liferay.dynamic.data.mapping.form.field.type/META-INF/resources/+(FieldBase|components)/**/*.soy\""
since we don't have any soy template to generate JS from.
What you will end up is just an es.js file.
Edit:
If you are using blade to generate the template, you can use this option to generate a react based component:
--js-framework react

Related

Material-UI Theme : How to manage order or priority the .MuiXXX classes are applied?

[EDIT April 19th]
I have created a CODESANDBOX to show the problem, of course, that doesn't occur in sandbox.
The only difference between this code and mine is that I have duplicated the code of the Button component in the SANDBOX example, whereas in my App the Button component is imported from a library (that belongs to the same yarn workspace as the app). The library is built with webpack and babel, excluding React and Material-UI
externals: {
react: "react",
"react-dom": "react-dom",
"react-router": "react-router",
"react-router-dom": "react-router-dom",
"#material-ui/core": "#material-ui/core",
"#material-ui/icons": "#material-ui/icons",
"#material-ui/lab": "#material-ui/lab",
"#material-ui/styles": "#material-ui/styles",
formik: "formik",
},
Inspecting the components in the Browser shows the difference when styling, between sandbox and my app :
on both sides, the class are applied to the component the same way:
in sandbox
in my app
but on sandBox, the MuiButtonBase-root background-color is overridden by the MuiButton-root background-color
whereas it is the opposite in my app. The MuiButton-root backGroundColor seems to be overriden bu the MuiButtonBase-root background-color
However, if I create a component RecreatedButton in the App by just importing the Button component of my UI Library, and re-exported it without changing anything (just passing a specific props the component is requested), then the styling is applied correctly, as in the sandbox example.
this is kind of weird to me...
Why such a behavior ?
just importing and rexporting as is the component
import {
Button as LibraryButton,
EButtonTypes,
IButtonProps,
} from "#mylibrairy/reactcomponentscommon"; <---- importing the button
import React from "react";
const RecreatedButton: React.FC<IButtonProps> = (
props: IButtonProps
): JSX.Element => {
return (
<LibraryButton type={EButtonTypes.BUTTON}>
{props.children}
</LibraryButton>
);
};
export { RecreatedButton };
Using both in app.ts. One got the theme, the other not
import { ThemeProvider } from "#material-ui/core/styles";
import {
Button as LibraryButton,
EButtonTypes,
IButtonProps,
} from "#mylibrairy/reactcomponentscommon"
import React from "react";
import AppBar from "../../UIComponents/AppBar";
import { RecreatedButton } from "../../UIComponents/Button";
import { MUITheme } from "./../../Theming/defaultTheme";
export const MainApp: React.FC = (): JSX.Element => {
return (
<ThemeProvider theme={MUITheme}>
<>
<AppBar />
<LibraryButton type={EButtonTypes.BUTTON}> I'm the library component, directly used as is, and background color is NOT CORRECT ></LibraryButton>
<RecreatedButton>
I'm recreated button, just rexporting the library component, and the backgroundcolor is correct !?!?{" "}
</RecreatedButton>
</>
</ThemeProvider>
);
};
finally I found one solution (not sure that it fixes the root cause as I still do not understand where it comes from).
I Guess it may helps some people here that are facing a similar issue with global theming in Material-Ui.
It turned out that I had to change the way to build my react/material-Ui components library #mylibrairy/reactcomponentscommon.
1- Make sure that in the library, all imports where such as import { Button} from "#material-ui/core" and not for example import Button from "#material-ui/core/Button"
2- Remove the usage of file-loader plugin in the .babelrc to make sure it doesn't change the way to import material-ui components
3- Push #material-ui/core and #material-ui/icons as a dev and peer dependencies in the package.json of the library.
4- Rebuilt the library using webpack and babel to compile typescript tsx to js.
All issues of priority seems to disappear (have done a lot of tests and checked in the chrome dev tools). In the example above, the .MuiButton-root class is well applied after the .MuiButtonBase-root one, thus overriding as expected the backgroundColor.
Would admit that I'm a little bit confused why this fixed the issue...
Rgds
For me, i just had to import "makeStyles" and "createStyles" from "#material-ui/core" not from "#material-ui/core/styles". i just did this and it fixed the issue but took me a lot of time to figure this out.
so import them like this:
import { makeStyles, createStyles } from "#material-ui/core";
not like this:
import { makeStyles, createStyles } from "#material-ui/core/styles";
You may try overriding default globals for MuiButtonBase
const theme = createMuiTheme({
props: {
// Name of the component ⚛️
MuiButtonBase: {
// The default props to change
root:{
backgroundColor: 'red'
}
},
},
});
function DefaultProps() {
return (
<ThemeProvider theme={theme}>
<Button>Change default props</Button>
</ThemeProvider>
);
}
Working sandbox here - https://codesandbox.io/s/override-button-base-7qwd5

How can I use a component like react-image-editor for cropping an image in react-admin

I am using React-Admin and I need to use/create a component for cropping an image (profile image) and storing it as a base64-image.
I found #toast-ui/react-image-editor better than other libraries but I have a problem with that. In React-Admin when we use ImageInput like this:
<ImageInput multiple label="Gallery" source="gallery" accept="image/*">
<ImageField source="src" title="title" />
</ImageInput>
the data can be loaded from the source (in Edit mode) and it will be stored there, but when we use other components like what I have mentioned, I need to know how can I pass the data as a source to that. It doesn't matter for me to use this library or any others... but I need one with simple usage.
Actually, my issue is with connecting the new component to the model that react-admin use.
I have recently written such a component, you can find it under the following link:
EditableImageComponent.
I don't use #toast-ui/react-image-editor for this, like you do, but ReactImageCrop, maybe the component will help you anyway. At the moment I have some bugs in the code (after the image is loaded, the crop has to be changed once before it is applied), but otherwise it works quite well so far.
Edit: In order to use this component in your EditView, simply call it like every other Input Component (it is assumed, that your target is called "imagename")
<EditableImage source="imagename" {...props} label="User image (Use the upload button to edit)"/>
I have use #toast-ui/react-image-editor as my image editor. i use modal to edit images in gallery. My simple code using react and bootstrap.
first import react image editor.
import 'tui-image-editor/dist/tui-image-editor.css';
import ImageEditor from '#toast-ui/react-image-editor';
if you use class component add this line.
export default class Example extends Component {
editorRef = React.createRef();
}
or if you use funtional component add this line.
function Example() {
const editorRef = React.createRef();
}
then call react image editor in the component.
<ImageEditor
ref={this.editorRef}
includeUI={{
loadImage: {
path: imagePath,
name: imageName,
},
theme: myTheme,
menu: [
"filter",
"crop",
"flip",
"resize",
"rotate",
"text",
],
initMenu: "filter",
uiSize: {
width: "100%",
height: "600px",
},
menuBarPosition: "left",
}}
cssMaxHeight={500}
cssMaxWidth={700}
selectionStyle={{
cornerSize: 20,
rotatingPointOffset: 70,
}}
usageStatistics={false}
/>

Extending styled components

I have a Field Formik's component, in order to apply custom CSS I do:
const Input = styled(Field)`
// CSS goes here
`
And use Input component, works fine. However I use exactly the same CSS in many places, so I've extracted those CSS to standalone styled-component called SuperInput
Now, how can extend style-componet? I need something like
const Input = styled(Field)`
// inlucde CSS from SuperInput component here
`
Example code.
import styled from 'styled-components'
const SuperInput = styled.input`
// CSS here
`
import { Field } from 'formik'
import { SuperInput } from 'styled_components/super_input'
const SomeFormComponent = () => (
<>
// How to use here <Field /> that has <SuperInput /> CSS applied?
</>
)
Basically you just need to spread or append inside the template literals to get it to work. you can keep the common CSS something like
import styled, { css } from "styled-components"
const commonCss = css`
background: red;
`
And can use it in your component like this:
const Input = styled(Field)`
// CSS goes here
${commonCss}
color: hotpink;
`;
const Input1 = styled(Field)`
${commonCss}
color: lightblue;
`;
This should allow you to use the common CSS in various styled components.
For more info, you can read through css styled component API
Edit:
Styled components create HOCs.
After the added superInput definition, I understand what you are trying to do. So your superInput is creating a button with the certain css properties which you are trying to reuse. In that case when you are using Field and trying to extend SuperInput which is a button doesnot make sense. Your Field component is by default a input element(text box), it can be checkbox, radio, file input also.Whatever CSS is written in superInput can be extracted the way I mentioned above and used at multiple places. The way you are trying to do is not the way styled component is designed. That's my understanding
Note : I may be wrong here about whether it is possible or not. But that's what i can say according to my awareness . Anyone Feel free to correct me if I am wrong.

Importing an image into an <src> tag in React.js when the image name is not known in advance

I have a component in React which displays (or doesn't at the moment) an image within an src tag.
The file path & file name of the image is passed via props after being selected by the user, so I can't do an import at the top of the file. Apparently, I should be able to do something like src={require(file)} but this causes webpack to throw a hissy fit and give the following error: Error: cannot find module "."
As an e.g., a typical filepath/filename I pass to the component is: '../../../images/black.jpg'
This is a stripped-down version of the code in question:
import React, { Component } from "react";
class DisplayMedia extends Component {
render() {
return (
<div className="imgPreview">
<img src={this.props.file} alt="piccy" />
</div>
);
}
}
export default DisplayMedia;
Depending on your set up...
If the images are dynamic (during production, images will be added, edited, or deleted):
I'd recommend a microservice that only handles images. I go in depth on how to approach such a service: Image returned from REST API always displays broken
If the images are static (during production, the images are bundled within the "bundle.js" file):
- I'd recommend importing all of the images within the component, creating an array of the imported images, and then utilizing the array index and React state to cycle through them. For example:
import React, { Component } from "react";
import Image1 from "../images/Image1.png";
import Image2 from "../images/Image2.png";
import Image3 from "../images/Image3.png";
const images = [Image1, Image2, Image3];
export default class ShowImage extends Component {
state = { index: 0 };
handleChange = ({ target: { value } }) => {
this.setState({ index: value });
};
render = () => (
<div className="container">
<h1>Utilizing Array Indexes</h1>
<select
style={{ marginBottom: 20 }}
value={this.state.index}
onChange={this.handleChange}
>
{images.map((val,idx) => (
<option key={idx} value={idx}>Image {idx + 1}</option>
))}
</select>
<img src={images[this.state.index]} />
</div>
);
}
While I can't create an exact codesandbox of the above, this working example should give you the basic idea: https://codesandbox.io/s/ovoo077685
You don't need to add require in src. When relative path is used it will go the images availale in your server but when url is given image will be loaded. You can find more info here
When using src as /images/black.jpg it will convert to localhost:3000/images/black.jpg

Vue component not rendered inside router-view

recently I've been using vue in frontend and vue-router with it to shape a SPA.
My problem is that I am not able to access a component defined in main Vue instance:
import ElementComponent from './Element';
const app = new Vue({
el: '#app',
router,
components: { Element: ElementComponent }
});
Whenever I do <element></element> within the #app scope the component gets rendered but I can not use the element inside a route component.
My routes are defined like this:
var routes = [
{ path: '/section/part', component: require('../views/Part') }];
And then provided to router instance:
new VueRouter({routes});
Breakpoint whenever I try to call <element></element> inside Part component template I get this in vuedevtools: [Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
(found in at C:\Users\Doe\project\js\views\Part.vue)
You need to import the component into the views/Part component. So do the same thing that you did in the main Vue instance, but only in the part component.
I believe if you want to make components global you need to do use
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
reference https://v2.vuejs.org/v2/guide/components.html#Registration

Resources