Use a vanilla JavaScript package in React app - javascript

I am aiming to use a Vanilla JavaScript package in a more sophisticated React app to build additional logic around the JavaScript package.
The JavaScript library is LabelStudio and docs can be found here: https://github.com/heartexlabs/label-studio-frontend
However, when I try to import the LabelStudio I get an error saying Module not found: Can't resolve 'label-studio' , as described here https://github.com/heartexlabs/label-studio-frontend/issues/55
Since my understanding of frontend code is limited, I am not sure whether this is something the developers did not expected users to do and just wanted them to use the entire library and customized instead of using the library as a component. My idea was to use the library as in the vanilla javascript example here:
<!-- Include Label Studio stylesheet -->
<link href="https://unpkg.com/label-studio#0.7.1/build/static/css/main.0a1ce8ac.css" rel="stylesheet">
<!-- Create the Label Studio container -->
<div id="label-studio"></div>
<!-- Include the Label Studio library -->
<script src="https://unpkg.com/label-studio#0.7.1/build/static/js/main.3ee35cc9.js"></script>
<!-- Initialize Label Studio -->
<script>
var labelStudio = new LabelStudio('label-studio', {
config: `
<View>
<Image name="img" value="$image"></Image>
<RectangleLabels name="tag" toName="img">
<Label value="Hello"></Label>
<Label value="World"></Label>
</RectangleLabels>
</View>
`,
interfaces: [
"panel",
"update",
"controls",
"side-column",
"completions:menu",
"completions:add-new",
"completions:delete",
"predictions:menu",
],
user: {
pk: 1,
firstName: "James",
lastName: "Dean"
},
task: {
completions: [],
predictions: [],
id: 1,
data: {
image: "https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg"
}
},
onLabelStudioLoad: function(LS) {
var c = LS.completionStore.addCompletion({
userGenerate: true
});
LS.completionStore.selectCompletion(c.id);
}
});
</script>
How can I make use of the above code in a React Component to facilitate dynamic data loading and use of state to customize the functions?

I don't have a solution making the npm module label-studio to work. I tried importing the dist file instead, but it errors
Expected an assignment or function call and instead saw an expression
So here's a workaround until the maintainers address this.
Copy the JS file from build/static/js, then place it in a script in the public folder on index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="%Your_Path_To_Label-Studio%/main.js"></script>
</body>
</html>
The script file defines a global function variable, so you can access it in React by using the window object. The useEffect hook is to make sure the initialization is only run once.
import React, { useEffect, useRef } from "react";
function App() {
const LabelStudio = window.LabelStudio; // label-studio script stores the api globally, similar to how jQuery does
const myLabelStudioRef = useRef(null); // store it and then pass it other components
useEffect(() => {
myLabelStudioRef.current = new LabelStudio("label-studio", {
config: `
<View>
<Image name="img" value="$image"></Image>
<RectangleLabels name="tag" toName="img">
<Label value="Hello"></Label>
<Label value="World"></Label>
</RectangleLabels>
</View>
`,
interfaces: [
"panel",
"update",
"controls",
"side-column",
"completions:menu",
"completions:add-new",
"completions:delete",
"predictions:menu",
],
user: {
pk: 1,
firstName: "James",
lastName: "Dean",
},
task: {
completions: [],
predictions: [],
id: 1,
data: {
image:
"https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg",
},
},
onLabelStudioLoad: function (LS) {
var c = LS.completionStore.addCompletion({
userGenerate: true,
});
LS.completionStore.selectCompletion(c.id);
},
});
}, []);
return (
<div className="App">
{/* Use Label Studio container */}
<div id="label-studio"></div>
</div>
);
}
export default App;
As far as storing the new instance of LabelStudio, there's many ways to go about it. You can store it as variable on the root component using either useState or useRef hooks and then pass it to child components. If you want to avoid manually passing variable down the component tree, then you need a state manager such as React Context or Redux.

This is the basic setup using LabelStudio as a module in a React component.
yarn add #heartexlabs/label-studio
import LabelStudio from '#heartexlabs/label-studio'
import { useEffect } from 'react'
import '#heartexlabs/label-studio/build/static/css/main.css'
const ReactLabelStudio = () => {
useEffect(() => {
new LabelStudio('label-studio', {
config: {...},
interfaces: [...],
user: {...},
task: {...},
onLabelStudioLoad: function(LS) {
const c = LS.annotationStore.addAnnotation({
userGenerate: true,
});
LS.annotationStore.selectAnnotation(c.id);
}
})
}, [])
return <div id="label-studio" />
}
export default ReactLabelStudio

Related

Next.js An update to Image inside a test was not wrapped in act(...)

I created a basic NextJS app using create-next-app then added Jest for testing. However, I am getting the error "An update to Image inside a test was not wrapped in act(...)" in my Jest test. It seems to be something to do with the Image component of NextJS updating after render, but I'm not quite sure what's the best way to test with it. I've included the Jest test and the problematic part of the component here:
import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
const Home: NextPage = () => {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</main>
</div>
)
}
export default Home
import React from 'react'
import { render, act } from '#testing-library/react'
import { axe } from 'jest-axe'
import Home from '#/pages/index'
it('should demonstrate this matcher`s usage with react testing library', async () => {
const { container } = render(<Home/>)
const results = await axe(container)
expect(results).toHaveNoViolations()
})
The error message also points to code at node_modules/next/client/image.tsx:353:3 being problematic, and looking at the source, that part seems to be a useLayoutEffect hook in the Image component...
Figured it out. Followed the solution mentioned at this article and it worked.
Install react-intersection-observer
In the Jest config (jest.config.js):
Add react-intersection-observer/test-utils' to the setupFilesAfterEnv
Create a jest.setupFiles.js and add to setupFiles in the in the customJestConfig
{
...
setupFiles: ['<rootDir>/jest.setupFiles.js'],
setupFilesAfterEnv: [
'<rootDir>/jest.setup.js',
'react-intersection-observer/test-utils',
],
...
}
In jest.setupFiles add:
import { defaultFallbackInView } from 'react-intersection-observer'
global.IntersectionObserver = jest.fn()
defaultFallbackInView(false)
And the error should go away. :)

Start embedded react app with the external parameters

I have a react app embedded in a webpage and the app should start with parameters it gets from the page. Now I pass the parameters on index.HTML file in a div but the problem is if the parameters are updated the app won't notice the changes.
index.HTML
<html>
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Sales flow</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div class="staringId" packageId="2" itemId="5" channelId="9"></div>
</body>
</html>
index.js
import './index.scss';
import App from './App'
const div = document.querySelector('.staringId')
ReactDOM.render(
<App domElement={div} />,
div
);
App.js
const App = ({ domElement }) => {
const package = domElement.getAttribute("packageId")
const item = domElement.getAttribute("itemId")
const channel = domElement.getAttribute("channelId")
const [data, setData] = useState({
packageId: '',
itemId: '',
channelId: '',
})
useEffect(() => {
setSelData({
packageId: package,
itemId: item,
channelId: channel,
})
}, [package, item, channel])
}
javascript file to embed the app
<div class="staringId" packageId="2" itemId="5" channelId="9"</div>
<script src="./dist/runtime-main.8d32315e.js"></script>
<script src="./dist/2.4c89be1e.chunk.js"></script>
<script src="./dist/main.a41deb26.chunk.js"></script>
My question is how I can pass some external parameters to the app and update(restart) the app after each update to the parameters.
The idea is to have only one top-level component in your HTML, and whatever parameters(props) you are trying to pass sit on the other components within the top-level . Following is the rough structure how it would look like.
index.html
<html>
<body>
<div id="app"></div>
</body>
</html>
index.js
import './index.scss';
import App from './App'
ReactDOM.render(
<App packageId="2" itemId="5" channelId="9" />, // you can choose to dynamically pass the value depends on what you get from page
document.getElementById("app")
);
App.js
const App = ( props ) => {
// props.packageId and etc are available here
// you can then choose to render it with these props value
return (
<MyOtherComponent otherProps="ifany" />
);
}
export default App;

react-json-schema tutorial does not show up in browser

I am completely new to web development (html/js) but would now like to use the react-json-schema package which works great in the provided sandbox.
However, I can't even get the tutorial to work. I have written an html, as given in the tutorial:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
<script type="text/jsx" src="react.js"></script>
</body>
</html>
the corresponding javascript file "react.js":
const Form = JSONSchemaForm.default;
const schema = {
title: "Test form",
type: "string"
};
ReactDOM.render((
<Form schema={schema} />
), document.getElementById("app"));
However, the schema simply does not show up in a browser when opening the html. There is no error message.
Things I have tried:
1.) importing the scripts from the cdn, so adding these lines in the html head:
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/#rjsf/core/dist/react-jsonschema-form.js"></script>
2.) re-installing npm and the react-json-schema, react and react-dom packages both locally and globally
3.) importing said packages in the js:
import react from React
import import ReactDOM from 'react-dom'
import Form from "#rjsf/core";
try below:
import Form from 'react-jsonschema-form';
class Index extends DataComponent{
constructor(props){
super(props);
this.state={
schema: {
type: 'object',
title: 'Info',
properties: {
task: {
type: 'string',
title: 'First Name'
}
}
}
}
}
render(){
return(
<Form
schema={this.state.schema}
/>
)
}
}
If you have installed the library then remove
//const Form = JSONSchemaForm.default;

How can i use a jquery/html/css component inside my react page

I have to use a complex and ready to use component developed using jQuery v2.2.4, javascript html and css inside one of my react pages, because its a fairly huge component with lots of features it really doesn't make sense for us to redevelop this inside react and make it reacty.
So my question is how can i use this inside react?
I simply want to render this component in the middle of my page, whats the best way to do this?, i'm a bit new to this and i don't know where to start.
This is the index.html which calls upon the said component, i thought maybe it helps in someway ?
<!DOCTYPE html>
<html>
<haed>
<title>Test</title>
</haed>
<body>
<link href="ol.css" rel="stylesheet" type="text/css" />
<link href="ls.css" rel="stylesheet" type="text/css" />
<script src="jquery.min.js"></script>
<script src="ol.js"></script>
<script src="ls.js"></script>
<script src="wss_map.js"></script>
<div id="map" style="width: 100%; height: 500px;"></div>
<script>
var i = 0;
var mp=$("#map").wssmap({
maps: [
{
name: 'm2',
type: 'osm',
order: 0,
zoomrange: { min: 0, max: 19 },
visible: true,
}
],
onClick:function(point) {
i++;
var options= {
longitude:point.longitude,
latitude:point.latitude,
label:i
}
mp.addMarker(options);
},
zoom:10
});
var marker=mp.addMarker({ longitude: 51.404343, latitude: 35.715298,label:'Testcase'});
</script>
</body>
</html>
Thanks.
Just follow this instructions:
1: install JQuery via npm :
npm install jquery --save
2: import $ from Jquery on top of you react component file:
import $ from 'jquery';
3: now you can call your JQuery component on maybe React componentDidMount()
componentDidMount() {
var mp=$("#map").wssmap({ ... })
}

Change Meta Title and Description using Vue.js

Is it possible to change anything higher than the body tag in Vue.Js? The contents for both these elements is currently stored in the JSON file that is attached to an element further down the DOM tree.
I need to try and inject a meta title and description that can be crawled by Google (ie. It injects, then renders before it gets crawled) and understand the issues with accessing the body element and higher up the DOM tree, as the current Vue JSON is injected using the App ID on a DIV lower down.
I have previously used some jQuery code to address this issue on a Square Space template in some previous work
jQuery('meta[name=description]').attr('content', 'Enter Meta Description Here');
PAGE HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="{{items[0][0].meta-desc}}">
<meta name="author" content="">
<title>{{items[0][0].meta-title}}</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<!-- Vue.js CDN -->
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<!-- Page List -->
<div class="container text-center mt-5" id="app">
<h1 class="display-4">Vue Page Output:</h1>
<h2>{{items[0][0].page1}}</h2>
</div>
<div class="container text-center mt-5">
<h3>Other Pages</h3>
Products
Contact Us
</div>
<!-- /.container -->
<script type="text/javascript">
const app = new Vue({
el: '#app',
data: {
items: []
},
created: function () {
fetch('test.json')
.then(resp => resp.json())
.then(items => {
this.items = items
})
}
});
</script>
</body>
</html>
JSON
[
[
{
"page1": "Company Name",
"meta-title": "Acme Corp"
"meta-desc": "Welcome to Acme Corp"
}
],
[
{
"products": "Product List"
}
],
[
{
"contactus": "Contact Us at Acme Corp"
}
]
Here is the code in action, the incoming JSON file comes in a fixed array format with the meta details alongside the body elements. Making this a bit more tricky.
https://arraydemo.netlify.com/
Since what you want to change is outside the area controlled by Vue, you just use ordinary DOM manipulation. It would be something like
created() {
fetch('test.json')
.then(resp => resp.json())
.then(items => {
this.items = items;
const descEl = document.querySelector('head meta[name="description"]');
const titleEl = document.querySelector('head title');
descEl.setAttribute('content', items[0]['meta-desc']);
titleEl.textContent = items[0]['meta-title'];
})
}
If you are using Vue Router, I believe that cleaner solution is using beforeEach hook:
const routes = [{ path: '/list', component: List, meta: {title:'List'} }]
router.beforeEach((to, from, next) => {
document.title = to.meta.title
next()
})
But it allows you set only static titles.
However, if you are looking for some SEO optimizations, Nuxt will probably solve most of your problems
Vue meta is an NPM package for meta-data management:
https://vue-meta.nuxtjs.org/
Example of how I use it in a vue page component:
export default {
name: "Contact",
metaInfo: function() {
return {
title: "My page meta title",
meta: [
{ name: 'description', content: "My page meta description" }
]
}
}
If you use Vue Router(that's what I'm doing) you can set Vue meta here so all your page can use it:
import Vue from 'vue'
import Router from 'vue-router'
import VueMeta from 'vue-meta'
Vue.use(Router)
Vue.use(VueMeta)
export default new Router({
mode: 'history',
base: '/',
routes: [
{
path: '/',
name: 'Home',
component: Home
},
If you want to change the title it is easy to change in vuejs.
In router.js file while creating route you can do something like this.
{
path:"/productdetail",
name:"productdetail",
component:ProductDetail,
meta: {
title : localStorage.getItem("title"),
}
}
router.beforeEach((toRoute,fromRoute,next) => {
window.document.title = toRoute.meta.title;
next();
})
use of localStorage will help you to change title dynamically.
unfortunately, meta description is not changing with the same method.

Categories