Worked through how to get Leaflet in Stimulus and a Leaflet plugin leaflet.control.opacity to load, but the plugin loads with an error.
[watch] build started (change: "app/javascript/controllers/control_opacity_controller.js")
✘ [ERROR] Cannot assign to "i" because it is a constant
node_modules/leaflet.control.opacity/dist/L.Control.Opacity.js:1:2656:
1 │ ...ue=100*t.layer.options.opacity):i=this._createRadioElement("leaf...
╵ ^
The symbol "i" was declared a constant here:
node_modules/leaflet.control.opacity/dist/L.Control.Opacity.js:1:2498:
1 │ ...document.createElement("label"),i=document.createElement("input"...
The plugin works in other contexts, but not here. The Stimulus controller
// app/javascript/controllers/control_opacity_controller.js
import { Controller } from "#hotwired/stimulus"
import L from "leaflet"
import Opacity from "leaflet.control.opacity"
export default class extends Controller {
static targets = [ "map" ]
connect() {
const m_color = new L.tileLayer('https://tile.mierune.co.jp/mierune/{z}/{x}/{y}.png', {
attribution:
"Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL.",
});
const m_mono = new L.tileLayer('https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png', {
attribution:
"Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL.",
});
const o_std = new L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
});
const t_pale = new L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png', {
attribution:
"<a href='http://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html' target='_blank'>国土地理院</a>",
});
const t_ort = new L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/ort/{z}/{x}/{y}.jpg', {
attribution:
"<a href='http://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html' target='_blank'>国土地理院</a>",
});
const map = L.map('maplco', {
center: [35.6831925, 139.7511307],
zoom: 13,
zoomControl: true,
layers: [m_mono],
});
//BaseLayer
const Map_BaseLayer = {
'MIERUNE Color': m_color,
'MIERUNE MONO': m_mono,
};
//AddLayer
const Map_AddLayer = {
'OSM': o_std,
'GSI Pale': t_pale,
'GSI Ort': t_ort,
};
//LayerControl
L.control
.layers(Map_BaseLayer, Map_AddLayer, {
collapsed: false,
})
.addTo(map);
//OpacityControl
L.control
.opacity(Map_AddLayer, {
label: 'Layers Opacity',
})
.addTo(map);
}
}
Related
I have created a small React app and I want to test it using Playwright component testing
I have 3 components: App -> ChildComponent -> ChildChildComponent
I want to render (mount) the ChildComponent directly, and make assertions on it, but when I do that, some ContextApi functions that are defined in the App in the normal flow, are now undefined as the App component is not part of the component test.
So i'v trying to render the ChildComponent together with a face ContextApi Provider and pass mocks of those undefined functions, and then I get an infinite render loop for some reason.
How can I go about this, as this use case is typical in react component test.
Here is the test with all my failed mocking attempts separated:
test.only("validate CharacterModal", async ({ page, mount }) => {
const data = ['some-mocked-irrelevant-data']
// const setCurrentCharacter = () => {};
// const setIsCharacterModalOpen = () => {};
// const setCurrentCharacterMocked = sinon.stub("setCurrentCharacter").callsFake(() => {});
// const setIsCharacterModalOpenMocked = sinon.stub("setCurrentCharacter").callsFake(() => {});
// const setCurrentCharacter = jest.fn();
// const setIsCharacterModalOpen = jest.fn();
// const setCurrentCharacter = (): void => {};
// const setIsCharacterModalOpen = (): void => {};
// const setIsCharacterModalOpen = (isCharacterModalOpen: boolean): void => {};
const AppContext = React.createContext<any>(null);
await page.route("**/users*", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(data),
});
});
const component = await mount(
<AppContext.Provider value={{ setCurrentCharacterMocked, setIsCharacterModalOpenMocked }}>
<CharacterModal />
</AppContext.Provider>
);
expect(await component.getByRole("img").count()).toEqual(4);
});
The beforeMount hook can be used for this. I recently added docs about this: https://github.com/microsoft/playwright/pull/20593/files.
// playwright/index.jsx
import { beforeMount, afterMount } from '#playwright/experimental-ct-react/hooks';
// NOTE: It's probably better to use a real context
const AppContext = React.createContext(null);
beforeMount(async ({ App, hooksConfig }) => {
if (hooksConfig?.overrides) {
return (
<AppContext.Provider value={hooksConfig.overrides}>
<App />
</AppContext.Provider>
);
}
});
// src/CharacterModal.test.jsx
import { test, expect } from '#playwright/experimental-ct-react';
import { CharacterModal } from './CharacterModal';
test('configure context through hooks config', async ({ page, mount }) => {
const component = await mount(<CharacterModal />, {
hooksConfig: { overrides: 'this is given to the context' },
});
});
In order to use MDX v2 with remark and rehype plugins in react applications created with Create React App v5 I have to use CRACO (v7) and especially its ability to handle a configuration that is returned as a promise/async function. The reason is that CRACO uses CJS module-based configuration, while the remark and rehype plugins are ES modules. Now I want to use MDXv2 with remark/rehype plugins in React Styleguidist too.
This is my craco.config.js:
const { addAfterLoader, loaderByName } = require('#craco/craco')
const mdxplagues = require('./mdxplugins.js')
// https://github.com/facebook/create-react-app/pull/11886#issuecomment-1055054685
const ForkTsCheckerWebpackPlugin =
process.env.TSC_COMPILE_ON_ERROR === 'true'
? require('react-dev-utils/ForkTsCheckerWarningWebpackPlugin')
: require('react-dev-utils/ForkTsCheckerWebpackPlugin');
module.exports = async (env) => {
const mdxplagueConfig = await mdxplagues()
return {
webpack: {
configure: (webpackConfig) => {
addAfterLoader(webpackConfig, loaderByName('babel-loader'), {
test: /\.(md|mdx)$/,
loader: require.resolve('#mdx-js/loader'),
/** #type {import('#mdx-js/loader').Options} */
options: mdxplagueConfig,
})
// https://github.com/facebook/create-react-app/pull/11886#issuecomment-1055054685
webpackConfig.plugins.forEach((plugin) => {
if (plugin instanceof ForkTsCheckerWebpackPlugin) {
plugin.options.issue.exclude.push({ file: '**/src/**/?(*.){spec,test,cy}.*' });
}
})
return webpackConfig
}
}
}
}
And this is my mdxplugins.js that returns an async function returing the needed plugin configurations:
const textrTypoEnDashes = (input) => {
return input
.replace(/ -- /gim, '–')
}
module.exports = async () => {
const remarkGfm = (await import('remark-gfm')).default
const remarkImages = (await import('remark-images')).default
const remarkTextr = (await import('remark-textr')).default
const remarkAccessibleEmoji = (await import('#fec/remark-a11y-emoji')).default
const rehypeSlug = (await import('rehype-slug')).default
const textrTypoApos = (await import('typographic-apostrophes')).default
const textrTypoQuotes = (await import('typographic-quotes')).default
const textrTypoPossPluralsApos = (await import('typographic-apostrophes-for-possessive-plurals')).default
const textrTypoEllipses = (await import('typographic-ellipses')).default
//const textrTypoEmDashes = (await import('typographic-em-dashes')).default
const textrTypoNumberEnDashes = (await import('typographic-en-dashes')).default
return {
remarkPlugins: [
remarkGfm,
remarkImages,
remarkAccessibleEmoji,
[remarkTextr, {
plugins: [
textrTypoApos,
textrTypoQuotes,
textrTypoPossPluralsApos,
textrTypoEllipses,
// textrTypoEmDashes,
textrTypoNumberEnDashes,
textrTypoEnDashes,
],
options: {
locale: 'en-us'
}
}],
],
rehypePlugins: [
rehypeSlug,
],
}
}
My problem is now that I need to apply this webpack/MDXv2 plugin configuration to styleguide.config.js (v13.0.0). I tried...
module.exports = async () => {
return {
...
}
}
...but this doesn't seem to be supported. A look into React Styleguidist's sources, namely scripts/config.ts seems to confirm this.
How can I synchronously resolve my MDX plugin configuration that I return via an async function from my mdxplugin.js CJS module within Styleguidist's CJS configuration module?
Update 1: ============
After remove "window.ShadyDOM={force:true};" then it worked. But this is causing other problem ;):
https://vaadin.com/forum/thread/17399734/leverage-browser-save-password-feature
#Override
public void configurePage(InitialPageSettings settings) {
// TODO Auto-generated method stub
settings.addMetaTag("mobile-web-app-capable", "yes");
settings.addMetaTag("apple-mobile-web-app-capable", "yes");
settings.addMetaTag("apple-mobile-web-app-status-bar-style", "black");
settings.addInlineWithContents(
InitialPageSettings.Position.PREPEND, "window.customElements=window.customElements||{};"
+ "window.customElements.forcePolyfill=true;" + "window.ShadyDOM={force:true};",
InitialPageSettings.WrapMode.JAVASCRIPT);
}
End Update. ============
I am trying to integrate Payal checkout to Vaadin 14.7 (Spring core, not spring boot).
Here is paypal-view.js
import { LitElement, html, css } from "lit-element";
let buttons;
let hasRendered = false;
class PaypalElement extends LitElement {
static get properties() {
return {
mood: {
type: String,
noAccessor: false,
hasChanged(newVal, oldVal) {
console.log("newVal " + newVal + " oldVal " + oldVal);
},
},
};
}
static get styles() {
return [
css`
mood_color {
color: green;
}
#paypal-button {
size: "responsive";
}
`,
];
}
firstUpdated(_changedProperties) {
let testFname = this.shadowRoot.getElementById("fname");
super.firstUpdated(_changedProperties);
if (buttons && buttons.close && hasRendered) {
buttons.close();
hasRendered = false;
}
buttons = window.paypal.Buttons({
// Set up the transaction
createOrder: function (data, actions) {
return actions.order.create({
application_context: {
brand_name: "Brand name",
user_action: "PAY_NOW",
//No shipping for in-tangible merchant
shipping_preference: "NO_SHIPPING",
payment_method: {
payee_preferred: "IMMEDIATE_PAYMENT_REQUIRED", // Pending status transactions will not be allowed if you pass this parameter.
payer_selected: "PAYPAL",
},
},
purchase_units: [
{
soft_descriptor: "CC_STATEMENT_NAME",
amount: {
value: "5.00",
},
},
],
});
},
// Finalize the transaction
onApprove: function (data, actions) {
const elementText = document.getElementById("fname");
return actions.order.capture().then(function (orderData) {
// Successful capture! For demo purposes:
console.log(
"Capture result",
orderData,
JSON.stringify(orderData, null, 2)
);
let transaction = orderData.purchase_units[0].payments.captures[0];
// Replace the above to show a success message within this page, e.g.
// const element = document.getElementById('paypal-button-container');
// element.innerHTML = '';
// element.innerHTML = '<h3>Thank you for your payment!</h3>';
// Or go to another URL: actions.redirect('thank_you.html');
document.getElementById("update-paypal-trans").innerHTML =
"update-paypal-trans = " + transaction.id;
// trigger lit event
testFname.value = transaction.id;
testFname.click();
document.getElementById("paypalelement").remove();
//console.log(elementText);
});
},
onError: function (error) {
console.log("onError", error, JSON.stringify(error, null, 2));
},
onCancel: function (data, actions) {
console.log("onCancel", data, JSON.stringify(data, null, 2));
document.getElementById("update-paypal-trans").innerHTML =
"testing 123";
// update shadow element
testFname.value = "12345 " + actions;
// trigger lit event
testFname.click();
},
});
// load paypal buttons and put them to element id="paypal-button-to-display" which is shadow dom
buttons
.render(this.shadowRoot.getElementById("paypal-button-to-display"))
.then(() => {
hasRendered = true;
})
.catch((err) => {
console.log(err)
// not mounted anymore, we can safely ignore the error
return;
});
}
// outside updates shadow element
updateShadow() {
this.shadowRoot.getElementById("test-update-shadow").innerHTML =
"test update shadow trans";
this.mood = "nice";
}
updateTask(e) {
console.log("updateTask: " + e);
}
updateTaskClick(e) {
console.log("updateTaskClick: " + e);
// call back-end
}
render() {
return html`
Web Components are
<span class="mood_color"> ${this.mood} and ${this.innerHTML}</span>!
<input
type="text"
id="fname"
name="fname"
value="${this.mood}"
#change=${(e) => this.updateTask(e.target.value)}
#click="${(e) => this.updateTaskClick(e.target.value)}"
/>
<div id="paypal-button-to-display"></div>
<br />
<div id="test-update-shadow">test-update-shadow-default</div>
<br />
<input
#click="${() => this.updateShadow()}"
id="myinput"
type="button"
value="update shadow button"
/>
`;
}
attributeChangedCallback(name, oldval, newval) {
super.attributeChangedCallback(name, oldval, newval);
console.log("attribute change: ", name, newval);
}
changeProperties() {
let randomString = Math.floor(Math.random() * 100).toString();
this.mood = "myProp " + randomString;
console.log("randomString change: ", randomString);
}
}
customElements.define("paypal-element", PaypalElement);
hello-world-view.ts
import { html, LitElement, customElement } from 'lit-element';
import './paypal-view';
import '#vaadin/vaadin-button';
import '#vaadin/vaadin-text-field';
#customElement('hello-world-view')
export class HelloWorldView extends LitElement {
createRenderRoot() {
// Do not use a shadow root
return this;
}
constructor() {
super();
}
render() {
return html`
<vaadin-text-field id="name" label="Your name"></vaadin-text-field>
<vaadin-button >Say hello</vaadin-button>
<paypal-elementt mood="great" id="paypalBut">hello customer</paypal-elementt>
`;
}
}
hello-world-view.ts
package com.lecompany.iad.view;
import com.lecompany.iad.app.MainView;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.littemplate.LitTemplate;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.template.Id;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouteAlias;
/**
* A Designer generated component for the stub-tag template.
*
* Designer will add and remove fields with #Id mappings but does not overwrite
* or otherwise change this file.
*/
#PageTitle("Hello World")
#Route(value = "hello", layout = MainView.class)
#Tag("hello-world-view")
#JsModule("./src/view/hello-world-view.ts")
public class HelloWorldView extends LitTemplate {
#Id
private TextField name;
// #Id
// private Button sayHello;
public HelloWorldView() {
UI.getCurrent().getPage().addJavaScript("https://www.paypal.com/sdk/js?client-id=AejZZjQsvxS299I2_LSnRkJStp0AsBzScCqbGK1_W6RNJssNR5NVnKYd97dM2kOJnRMF1u1ldLGjOlZ5¤cy=USD");
// sayHello.addClickListener(e -> {
// Notification.show("Hello " + name.getValue());
// });
}
}
And here is error:
I tested above codes in Spring boot, then it works fine.
but it got error in the normal spring code. Any advice on this ?
There is reported issue for Paypal.
Paypal reported issue
It is a Vaadin bug and reported here:
Vaadin bug
I'm using gulp to pack js. Also I wanna using other libraries like materializecss. So I'm just importing it in my main.js. But after builing gulp I faced an error:
events.js:165
throw er; // Unhandled 'error' event
^
SyntaxError: 'import' and 'export' may appear only with 'sourceType: module'
main.js
import "materialize-css";
const Vue = require("vue");
const $ = require("jquery");
const v = new Vue({
el: '#app',
ready: function () {
this.loadData();
},
data: {
message: 'Hfssfsfello Vue.13js!',
serverData: null
},
methods: {
loadData: function (viewerUserId, posterUserId) {
const that = this;
$.ajax({
contentType: "application/json",
dataType: "json",
url: window.ServerUrl + "/Home/GetData",
method: "GET",
success: function (response) {
console.log(response);
that.$data.serverData = response;
},
error: function () {
console.log("Oops");
}
});
}
}
})
gulpfile
const gulp = require('gulp');
const gutil = require('gulp-util');
var babel = require('gulp-babel');
var minify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
const fs = require('fs');
const path = require('path');
const browserify = require('browserify');
const watchify = require('watchify');
const fsPath = require('fs-path');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var es2015 = require('babel-preset-es2015');
function getFolders(dir) {
return fs.readdirSync(dir)
.filter(function (file) {
return fs.statSync(path.join(dir, file)).isDirectory();
});
}
const paths = [
process.env.INIT_CWD + '\\ViewModels\\home',
process.env.INIT_CWD + '\\ViewModels\\home\\components',
process.env.INIT_CWD + '\\ViewModels\\common\\components'
];
function watchFolder(input, output) {
var b = browserify({
entries: [input],
cache: {},
packageCache: {},
plugin: [watchify],
basedir: process.env.INIT_CWD,
paths: paths
});
function bundle() {
b.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(minify())
.on('error', gutil.log)
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(output));
gutil.log("Bundle rebuilt!");
}
b.on('update', bundle);
bundle();
}
function compileJS(input, output) {
// set up the browserify instance on a task basis
var b = browserify({
debug: true,
entries: [input],
basedir: process.env.INIT_CWD,
paths: paths
});
return b.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(babel({ compact: false, presets: ['es2015'] }))
// Add transformation tasks to the pipeline here.
.pipe(minify())
.on('error', gutil.log)
//.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(output));
}
const scriptsPath = 'ViewModels';
gulp.task('build', function () {
var folders = getFolders(scriptsPath);
gutil.log(folders);
folders.map(function (folder) {
compileJS(scriptsPath + "//" + folder + "//main.js", "Scripts//app//" + folder);
});
});
gulp.task('default', function () {
var folders = getFolders(scriptsPath);
gutil.log(folders);
folders.map(function (folder) {
watchFolder(scriptsPath + "//" + folder + "//main.js", "Scripts//app//" + folder);
});
});
By the way, I'm very new to vue js framework and I'm trying to integrate this framework with ASP .NET MVC. And I can't figure out what I'm doing wrong? I'd be very appreciated if somebody answers me.
Surely not an answer, but I refused to use gulp. Instead I've installed webpack. Now it's alright!
I am developing a simple POC to display the current position.
The program receives the information from the geolocation.watchLocation however the location is not bind and displayed on the screen until I press the button STOP Monitoring. To be noted that the log is correctly showing the coordinates
JS: Start Monitoring ...
JS: Location: 49.6411839:6.0040451
JS: Stop Monitoring ...
import {Component} from "#angular/core";
import {Router} from "#angular/router";
import geolocation = require("nativescript-geolocation");
#Component({
selector: "TrackingPageSelector",
template:`
<StackLayout>
<Button [text]="watchId==0 ? 'Start Monitoring' : 'Stop Monitoring'" (tap)="buttonStartStopTap()"></Button>
<Label class="labelValue" [text]="latitude"> </Label>
<Label class="labelValue" [text]="longitude"> </Label>
</StackLayout>`
})
export class TrackingPageComponent {
latitude: number = 0;
longitude: number = 0;
watchId: number = 0;
constructor(private router: Router) {}
ngOnInit() {
if (!geolocation.isEnabled()) geolocation.enableLocationRequest();
}
public buttonStartStopTap() {
if (this.watchId != 0) {
console.log('Stop Monitoring ...');
geolocation.clearWatch(this.watchId);
this.watchId = 0;
} else {
console.log('Start Monitoring ...');
let _self = this;
this.watchId = geolocation.watchLocation(
function (loc) {
if (loc) {
_self.latitude = loc.latitude;
_self.longitude = loc.longitude;
console.log(`Location: ${_self.latitude}:${_self.longitude}`);
}
},
function(e){
this.errorMsg = e.message;
},
{desiredAccuracy: 3, updateDistance: 10, minimumUpdateTime: 1000 * 3}); // Should update every X seconds
}
}
}
If you use ()=> instead of function () then you don't need the _self hack.
It looks like you need to invoke change detection manually. NgZone seems not to cover geolocation.watchLocation()
constructor(private cdRef:ChangeDetectorRef) {}
...
this.watchId = geolocation.watchLocation(
(loc) => {
if (loc) {
this.latitude = loc.latitude;
this.longitude = loc.longitude;
this.cdRef.detectChanges();
console.log(`Location: ${this.latitude}:${this.longitude}`);
}
},
(e) => {
this.errorMsg = e.message;
},
or alternatively zone.run(() => ...) like
constructor(private zone:NgZone) {}
...
this.watchId = geolocation.watchLocation(
(loc) => {
if (loc) {
this.zone.run(() => {
this.latitude = loc.latitude;
this.longitude = loc.longitude;
console.log(`Location: ${this.latitude}:${this.longitude}`);
});
}
},
(e) => {
this.errorMsg = e.message;
},
If you only change local fields cdRef.detectChanges() is the better option, if you call methods that might modify some state outside of the current component, zone.run(...) is the better option.