Integrate jsPDF in Nativescript - nativescript

I researched that using jsPDF, json response cna be converted into PDF and then can be downloaded. Is there any way to integrate JsPDF in nativescript application?

Check the following POC with jsPDF and NativeScript, you need to override some global variables required by jsPDF and install some packages from npm:
global['window'] = {
'document': {
'createElementNS': () => { return {} }
}
};
global['document'] = {
'createElement': (str) => { return {} }
};
global['navigator'] = {};
import * as base64 from 'base-64';
import * as utf8 from 'utf8';
import * as jsPDF from 'jspdf';
global['btoa'] = (str) => {
var bytes = utf8.encode(str);
return base64.encode(bytes);
};
Example: https://play.nativescript.org/?template=play-ng&id=OGrw9s&v=8
By other hand, you can use a NativeScript shim for aotb / btoa functions, to avoid install more packages from npm (Only install jspdf and #types/jspdf): https://play.nativescript.org/?template=play-ng&id=OGrw9s&v=13
As you can see in the logic of the component, you need to import the file and modify some global variables:
require('../base64')
global['window'] = {
'document': {
'createElementNS': () => { return {} }
},
'atob': global['atob']
};
global['document'] = {
'createElement': (str) => { return {} }
};
global['navigator'] = {};
It would be much better to create a fork of jsPDF with that solution.
And later you can create your PDF:
generatePDF() {
var doc = new jsPDF('p', 'pt');
doc.setFontSize(26);
doc.text(40, 50, "My first PDF with NativeScript!");
//Add image
doc.addImage(imgData, 'JPEG', 20, 90, 300, 300)
var base64 = doc.output('datauristring')
dialogs.alert({
title: "PDF - Base64",
message: base64,
okButtonText: "Copy text"
}).then(() => {
clipboard.setText(base64)
});
}

Related

How do I render embedded code blocks from Contentful with Prism in Astro?

I decided to try out Astro by building a web development blog integrated with contentful. I got everything working except for the code blocks. I create an embedded entry for the code block in my content model, which returns the following nodeType:
{
nodeType: 'embedded-entry-block',
data: {
target: {
metadata: [Object],
sys: [Object],
fields: {
code:
'```html\n<!DOCTYPE html>\n<html>\n<head>\n<title>My First Astro App</title>\n</head>\n <body>\n<h1>Hello, World!</h1>\n</body>\n</html>\n```'
}
}
},
content: []
}
I cannot figure out how to use documentToHtmlString from "#contentful/rich-text-html-renderer" to display the code block in my blog with a themed Prism component.
I passed a custom rendering component for the embedded entry to the documentToHtmlStringoptions, and extracted the lang and HTML code, but then I could not figure out how to return a themed Prism code block. However, I did manage to get it working without styling using the runHighlighterWithAstro function from the #astrojs/prism library.
---
import { documentToHtmlString } from "#contentful/rich-text-html-renderer";
import { BLOCKS } from "#contentful/rich-text-types";
import ContentLayout from "#layouts/ContentLayout.astro";
import BlogPostMeta from "#components/BlogPostMeta.astro";
import { BlogPost, contentfulClient } from "#lib/contentful";
import { Prism } from "#astrojs/prism";
import { runHighlighterWithAstro } from "#astrojs/prism/dist/highlighter";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
function extractCode(str: string): { lang: string; code: string } | null {
const match = str?.match(/```(\w+)\n([\s\S]+)\n```/);
if (match) {
return {
lang: match[1],
code: match[2],
};
}
return null;
}
const renderOptions = {
renderNode: {
[BLOCKS.EMBEDDED_ENTRY]: (node: any) => {
const { code } = node.data.target.fields;
const extractedCode = extractCode(code);
if (extractedCode) {
const { lang, code } = extractedCode;
const { classLanguage, html } = runHighlighterWithAstro(lang, code);
return `<pre class=${classLanguage} set:html=${html}></pre>`;
}
return "";
},
},
};
const pages = entries.items.map((item) => ({
params: { slug: item.fields.slug },
props: {
title: item.fields.title,
description: item.fields.description,
content: documentToHtmlString(item.fields.content, renderOptions),
date: new Date(item.fields.date).toLocaleDateString(),
},
}));
return pages;
}
const { slug } = Astro.params;
if (typeof slug !== "string") {
throw Error(`Slug should be a string. Received: ${slug}`);
}
const { description, content, title, date } = Astro.props;
---
<ContentLayout title={title} date={date}>
<BlogPostMeta
title={title}
description={description}
publishDate={date}
pagePath={`/posts/${slug}`}
slot='meta'
/>
<body>
<h1>{title}</h1>
<time>{date}</time>
<article slot='content' set:html={content} />
</body>
</ContentLayout>
I wanted to use a component, CodeBlockthat contains my styled Prism element. Unfortunately, I may be misunderstanding something about Astro, coming from React land.
---
import { Prism } from "#astrojs/prism";
const { language, code } = Astro.props;
---
<Prism lang={language} code={code} />
<style is:global>...
</style>

Visual regression in Cypress

I am doing a visual regression :
code: plugins/indexts
on('task', {
graphicsMatch: ({ img1Path, img2Path, diffImgPath }) => {
const img1 = PNG.sync.read(fs.readFileSync(img1Path));
const img2 = PNG.sync.read(fs.readFileSync(img2Path));
const { width, height } = img1;
const diff = new PNG({ width, height });
let pixelCount = pixelmatch(img1.data, img2.data, diff.data);
fs.writeFileSync(diffImgPath, PNG.sync.write(diff));
return pixelCount;
},
});
stepdef:
And('I capture the screenshot of {string}', (editorDocument: string) => {
cy.wait(5000);
cy.captureScreen(editorDocument).as('generatedFileName');
});
Then(
'the generated pdf is as expected {string} and {string}',
(nameOfFeatureFile: string, editorScreenshot: string) => {
filesApi.comparePixelDiff(
nameOfFeatureFile,
editorScreenshot,
Cypress.currentTest.title,
);
},
);
API's:
comparePixelDiff(
nameOfFeaturefile: string,
actualFileName: string,
expectedFileName: string,
) {
cy.get('#generatedFileName').then((receivedFileName) => {
const sourceFilePath = `${FilesApi.screesnShotsFolder}${FilesApi.visualTestingFeaturesFolder}/${nameOfFeaturefile}/${actualFileName}${FilesApi.graphicExt}`;
const expectedFilePath = `${FilesApi.expectedExportsFolder}${expectedFileName}${FilesApi.graphicExt}`;
const diffFilePath = `${FilesApi.actualExportsFolder}${actualFileName}-${FilesApi.graphicsDiffSufix}${FilesApi.graphicExt}`;
cy.task('graphicsMatch', {
img1Path: sourceFilePath,
img2Path: expectedFilePath,
diffImgPath: diffFilePath,
}).then((pixelsDiff) => {});
});
}
}
i am using pixelmatch here,i have used viewport as 1280x720 in json file locally it works fine but fails on CI as the resolution of screenshot is not persistent
i have even tried cy.viewport(1280, 720) before capturescreenshot it didnt work as well.
How do i fix this issue, please help.

Laravel Inertia using InertiaProgress with Axios returns: Cannot read property 'defaultPrevented' of undefined

I am using current versions of Laravel & Inertia.js. I am also using IntertiaProgress and looking to use it during a file upload using axios
The files all upload OK but the console shows:
app.js:33316 Uncaught (in promise) TypeError: Cannot read property 'defaultPrevented' of undefined
Tracing this back it points to here in the source of InertiaProgress:
start(event) {
Promise.resolve().then(() => {
if (event.defaultPrevented) {
return
}
this.inProgress++
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
Nprogress.set(0)
Nprogress.start()
}, this.delay)
})
},
If I console.log(event) I get undefined
My upload() method:
upload(files) {
let url = this.$route('library.upload');
this.$root.showLoading = true;
const uploadConfig = {
timeout: 10000,
onUploadProgress: function(progressEvent) {
let percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
InertiaProgress.progress(percentCompleted)
}
}
InertiaProgress.start()
axios.post(url, {
files,
},
uploadConfig
).then(res => {
let files = res.data.files
if (files.length) {
this.filesList = cloneDeep(files)
}
this.newFiles = []
InertiaProgress.finish()
this.$root.showLoading = false
}).catch(err => {
console.log(err)
InertiaProgress.finish()
this.$root.showLoading = false
})
},
Any help is appreciated
You could use NProgress directly.
Instead of:
InertiaProgress.progress(percentCompleted)
Like this:
NProgress.progress(percentCompleted)
See also: https://inertiajs.com/progress-indicators

CKEditor 5 - writer.setAttribute('title', ...) on img element doesn't work

I am creating a plugin for CKEditor 5, and I can't figure out how to set the title attribute on an <img> element using writer.setAttribute('title', ...).
I have tried stuff like schema.extend but to no avail.
The thing is, code works flawlessly when operating on the alt attribute.
Am I missing something?
My plugin code:
const ButtonView = require('#ckeditor/ckeditor5-ui/src/button/buttonview').default;
const imageIcon = require('#ckeditor/ckeditor5-core/theme/icons/low-vision.svg').default;
export default class ImageTextTitle extends Plugin {
init() {
const editor = this.editor;
editor.ui.componentFactory.add('imageTextTitle', locale => {
const view = new ButtonView(locale);
view.set({
label: 'Insert image title',
icon: imageIcon,
tooltip: true
});
view.on('execute', () => {
const newTitle = prompt('New image title');
const selection = editor.model.document.selection;
const imageElement = selection.getSelectedElement();
if (newTitle !== null) {
editor.model.change(writer => {
writer.setAttribute('title', newTitle, imageElement);
});
}
});
return view;
});
}
}

Read excel files in Cypress

I am new to Cypress. How to read data from excel files using Cypress? Searched in google but could not find useful answers.
In cypress you can create cypress task to read xlsx file with SheetJS library.
Usage
cypress\integration\read-xlsx.spec.js
context('Xlsx file', () => {
it('Read excel file', () => {
cy.task('readXlsx', { file: 'my-excel.xlsx', sheet: "Sheet1" }).then((rows) => {
expect(rows.length).to.equal(543)
// expect(rows[0]["column name"]).to.equal(11060)
})
})
})
Need to install xlsx
$ npm install xlsx
Create read excel fuction
cypress\plugins\read-xlsx.js
const fs = require('fs');
const XLSX = require('xlsx');
const read = ({file, sheet}) => {
const buf = fs.readFileSync(file);
const workbook = XLSX.read(buf, { type: 'buffer' });
const rows = XLSX.utils.sheet_to_json(workbook.Sheets[sheet]);
return rows
}
module.exports = {
read,
}
Use function as Cypress task (plugin)
cypress\plugins\index.js
const readXlsx = require('./read-xlsx')
module.exports = (on, config) => {
on('task', {
'readXlsx': readXlsx.read
})
}
Here is an instruction how to use excel as source for cypress tests https://medium.com/#you54f/dynamically-generate-data-in-cypress-from-csv-xlsx-7805961eff55
First you need to conver your xlsx file to json with Xlsx
import { writeFileSync } from "fs";
import * as XLSX from "xlsx";
try {
const workBook = XLSX.readFile("./testData/testData.xlsx");
const jsonData = XLSX.utils.sheet_to_json(workBook.Sheets.testData);
writeFileSync(
"./cypress/fixtures/testData.json",
JSON.stringify(jsonData, null, 4),
"utf-8"
);
} catch (e) {
throw Error(e);
}
Then import json file and loop over each row and use the data in the way you want. In this example it tries to log in to a system.
import { login } from "../support/pageObjects/login.page";
const testData = require("../fixtures/testData.json");
describe("Dynamically Generated Tests", () => {
testData.forEach((testDataRow: any) => {
const data = {
username: testDataRow.username,
password: testDataRow.password
};
context(`Generating a test for ${data.username}`, () => {
it("should fail to login for the specified details", () => {
login.visit();
login.username.type(data.username);
login.password.type(`${data.password}{enter}`);
login.errorMsg.contains("Your username is invalid!");
login.logOutButton.should("not.exist");
});
});
});
});
For me the first answer pretty much worked. But i had to make a small fix.
Use function as Cypress task (plugin)
cypress/plugins/index.js
const readXlsx = require('./read-xlsx')
module.exports = (on, config) => {
on('task', {
'readXlsx': readXlsx.read
})
}
when i used this code i got the below error in cypress.
CypressError
cy.task('log') failed with the following error:
The task 'log' was not handled in the plugins file. The following tasks are registered: readXlsx
and the below fix worked
const readXlsx = require('./read-xlsx')
module.exports = (on, config) => {
on('task', {
'readXlsx': readXlsx.read,
log (message) {
console.log(message)
return null
}
})
}

Resources