How do you use an SVG inline in React using Parcel 2? - reactjs

Previously in Parcel v1 you could just use something like the #svgr/parcel-plugin-svgr plugin for Parcel. This would give you the ability to use SVGs inline like when using CRA:
import Star from './star.svg'
const App = () => (
<div>
<Star />
</div>
)
Can anyone help figure out a way to do this in Parcel 2?

Parcel2 provides the #parcel/transformer-svg-react plugin to accomplish this.
Here's what you need to do:
Install it:
yarn add #parcel/transformer-svg-react --dev
Add a .parcelrc file at the root of your project that defines a named pipleine that uses this plugin:
{
"extends": "#parcel/config-default",
"transformers": {
"jsx:*.svg": ["...", "#parcel/transformer-svg-react"]
"jsx:*": ["..."]
}
}
(The "..." entry should actually be typed into the .parcelrc file - it tells parcel "process these kinds of assets as usual, and only after you are done, transform it into a react component.")
Update (5/2022) I added the "jsx:*": ["..."] line, which appears to currently be a necessary workaround to avoid this bug.
Another current "gotcha" is that if the SVG files that you want to transform into react components contain inline style tags (e.g. <path style="fill:red" ...>, you'll run into this bug. To avoid it, you'll want to remove the #parcel/transformer-svg plugin from your pipeline by modifiying the first transformer line to read "jsx:*.svg": ["#parcel/transformer-svg-react"] (i.e. remove the ...).
Use the named pipeline in your import statements:
import Star from 'jsx:./star.svg'
const App = () => (
<div>
<Star />
</div>
)
(optional, if you use TypeScript). Let TypeScript know about the jsx: named pipeline by using a wildcard module declaration. In a d.ts file that's a part of your typescript project, write this:
declare module "jsx:*.svg" {
import { ComponentType, SVGProps } from "react";
const SVGComponent: ComponentType<SVGProps<SVGSVGElement>>;
export default SVGComponent;
}
See parcel 2 documentation.

Related

How to enable TypeScript intellisense in VSCode for a codebase in JavaScript when doing module augmentation for a third-party library?

In a standard create-react-app configuration, there’s a third-party component that I can import when needed:
import { Button } from '#mui/material'
// […]
<Button variant="|"></Button>
The library is fully-typed, so hitting Ctrl+Space in VSCode when the cursor is at | will show me a list of possible string variants that I can use. I could add another variant by creating a local component that wraps the #mui/material/Button:
// LocalComponent.js
import React from 'react'
import { Button } from '#mui/material'
// Do something to actually add a variant called `new`.
export const LocalComponent = React.forwardRef((props, ref) => {
return (
<Button ref={ref} {...props}>
{props.children}
</Button>
)
})
// LocalComponent.d.ts
import type { ButtonProps } from '#mui/material'
interface Props extends ButtonProps {
/** #default 'text' */
variant?: 'text' | 'outlined' | 'contained' | 'new'
}
export declare function LocalComponent(props: Props): React.FunctionComponent
If I import LocalComponent, I can get intellisense for the new variant, as well as the original Button’s props. The library also has a feature that allows me to create new variants without having to wrap the library’s components, but I’m having problems with intellisense when I use it—even though the new variant is implemented, I see only the original variants. The library exposes an interface specifically for module augmentation, so I should be doing something like this:
declare module '#mui/material/Button' {
interface ButtonPropsVariantOverrides {
new: true;
}
}
ButtonPropsVariantOverrides is supposed to be merged with the variant property of the interface that defines all props for the Button, essentially turning it into variant?: 'text' | 'outlined' | 'contained' | 'new', but it seems that TypeScript doesn’t see this module augmentation. I tried the following setup:
// jsconfig.json
{
"compilerOptions": {
"baseUrl": "src",
"typeRoots": [
"./src/types", "node_modules/#types"
]
},
"include": ["src"]
}
Not sure if typeRoots does anything in jsconfig—it’s not listed as an available option of compilerOptions, but since jsconfig is almost tsconfig, I tried it anyway.
.
├── src/
│ └── types/
│ └── #mui/
│ └── material/
│ └── Button/
│ └── index.d.ts // Contains the module augmentation from above.
└── jsconfig.json
As well as this one:
.
├── src/
│ └── types/
│ └── index.d.ts // Contains the module augmentation from above.
└── jsonfig.json
I also made sure to restart the TS server every time I changed something. Where to put module augmentations in general? Is there anything wrong with how I set up jsconfig.json? Here’s a CodeSandbox of what I’ve just described.
Thank you for your time.
It seems that when a file with module augmentation doesn’t have import/export statements, you’re defining an ambient module—a shape that does not have an implementation—that you have to import manually where needed. To augment a module and make it globally available, the declaration file should have an import or export statement.
What’s bizarre is that the only thing that matters is for the declaration file to be a module with at least one import/export statement, which could be absolutely anything—if I’m augmenting #mui/material/Button, I don’t have to import '#mui/material/Button' specifically, I could write some useless arbitrary export, like export default true, and TypeScript would recognize the file anyway.
I did a quick test on CodeSandbox to see if the same applies to TypeScript projects—it seemingly does. It worked on CodeSandbox without the typeRoots property in tsconfig.json, but add it in case of problems.
Where to put module augmentations in general? Is there anything wrong with how I set up jsconfig.json?
Module augmentation can be in any folder you like, but that directory (or its ancestor) should be in the jsconfig’s include property, and if its ancestor has already been added to include, there’s no need to list the directory with types separately. Although unnecessary, you might want to separate augmented packages into folders containing index.d.ts—do not name the declaration files differently, or TypeScript will ignore them. Using my question as an example, one option is to put the module augmentation into ./src/types/#mui/Button/index.d.ts. Lastly, the typeRoots property, indeed, does nothing when added to jsconfig.json, so there’s no need to use it.
is better if you use tsconfig.json
if you use tsconfig and JSDoc example
your code can look like that
there are some properties of the tsconfig.json that we need to focus on. and the must important is baseUrl it is used for
Base directory to resolve non-absolute module names.
so take express for example
import express from "express";
with baseUrl we tell typescript where to look for them express types the tsc will prefix the imports paths like so
import express from "./node_modules/express";
per say... I choose express for a reason because quite often there are packages which does not includes them types, but there is a huge community that can have done them for us, to use them we need to import them separately
$ npm i -D #types/express
there will be some times in which you'll need to override them types to do so you need to let tsc where to look for them overrides with paths to do it properly you need to create a dir at the root of yow project with name types
project
--node_modules
--src
--types
--tsconfig.json
something important to mention is that inside of your types dir you need to create a dir with the same identifier as the one used in the import statement to keep with our example it will look like this
project
--node_modules
--src
--types
----express
------index.d.ts
--tsconfig.json
and its proper file needs to be call index.d.ts
lets add a new property to the request method of express userIp the new property will refer to the client ip address so it needs to be a string and it needs to be inside the req property
so let's override it
/**
* Creates an Express application. The express() function is a top-level function exported by the express module.
*/
declare function e (): core.Express;
declare namespace e {
interface Request<P = EnetoParams, ResBody = any, ReqBody = any, ReqQuery = core.Query> extends core.Request<P, ResBody, ReqBody, ReqQuery> {
/** ip del usuario que solicito la peticion */
userIp: string;
}
}
export = e;
now tell tsc where to find it. paths is used for it
{
"compilerOptions": {
"baseUrl":"./node_modules",
"paths": {
"express":["./#types/express/index.d.ts","../types/express/index.d.ts"]
}
}
}
the format is
"paths": {
"name used in the import statement":[
"path to the original definitions",
"path to the override",
"if any other exists"
]
}
if you do not want to do it for all them changes and stuff you can use wild cards
"paths": {
"*": ["./*/index.d.ts", "./*/types.d.ts", "./*/types", "./#types/index.d.ts","./#types/types.d.ts"]
}
we start them paths with ./ because our baseUrl is ./node_modules now tell the tsc where to find our own types or definitions
{
// baseUrl is ./node_modues thats why we need to get out of that dir and get inside types dir
"typeRoots": ["../types/index.d.ts"]
}
the index.d.ts
declare module "my-package-name" {
interface SomeObj {
prop1: string;
prop2: string;
}
}
the way of using it in your code like so
import { SomeObj } from "my-package-name";
const myVar: SomeObj = {};

REACT I want to create an ARRAY with the names of the files of the images in the PUBLIC FOLDER

REACT I want to create an ARRAY with the names of the files of the images in the PUBLIC FOLDER but can`t reach them because they aren't in the SCR folder.
Surely is something basic but i'm missing it.
enter image description here
To access the public folder, you have to use process.env.PUBLIC_URL or %PUBLIC_URL%:
<img src="%PUBLIC_URL%/images/some_image.png" />
Or, alternatively using Javascript as you want an array:
const images = ['image_1.png', 'image_2.png']
return images.map(image => <img src=`${process.env.PUBLIC_URL}/images/${image}`
As far as I know, there is no way to iterate through all the files in the public folder as webpack has no access to it. You would have to manually enumerate them.
If you instead use the src folder, it is possible to use import to dynamically using Regez. It looks like this (taken from here):
function importAll(r) {
let images = {};
r.keys().map((item, index) => { images[item.replace('./', '')] = r(item); });
return images;
}
const images = importAll(require.context('./images', false, /\.(png|jpe?g|svg)$/));
By the way, unless you have a strong reason not to use the src folder (for example, images are used outside of Javascript or outside of the React project), it is recommended to add all files inside the src folder as webpack can do additional optimizations, such as inlining (ie: using Data URI) images.
I'm not sure I understood your question, you want to dynamically create an array in the code inside your ReactJS app?
For instance, if '/public/image' contains 'img1.png', 'img2.png', you'd get
myArray = ['img1.png', 'img2.png']

Listing files to generate dynamic components with list

I want a scalable solution, using React, to list some pages and automatically using the last one as my main page.
I have my 'legals mention' page, which will be regularly updated with new content.
import { useTranslation } from "react-i18next";
function Legals() {
const { t } = useTranslation('common');
return (
<div>
<h1>Legals</h1>
<span>Version 2.1 - 4th revision</span>
<span>· Effective September 30, 2020</span>
<button>Archived versions</button>
{...}<Content>{...}
</div>
)}
export default Legals;
So this is supposed to display the most recent version, and contains a button with a list to older versions.
I was thinking of using i18n and check in a folder for the last version, is it the best way to do it?
How to accomplish it, at least in pseudocode?

Troubleshooting Typescript definition file for React Component

I'm currently using React 16.4.1 and Typescript 2.9.2. I'm trying to utilise the reaptcha library:
https://github.com/sarneeh/reaptcha
Which I import like this:
import * as Reaptcha from 'reaptcha';
As there are no type definitions shipped, building currently gives me this (expected) error:
TS7016: Could not find a declaration file for module 'reaptcha'. '...' implicitly has an 'any' type.
Despite this error, the code "works", but I've been trying for a long time to build just enough of a definition file to suppress the error. There seems to be a lot of different ways to do this based on how the library is export and I don't seem to have hit the right combination. For this definition:
declare module "reaptcha" {
import * as React from "react";
export default class Reaptcha extends React.Component<any, any> {
}
}
Rather than solving the issue, this just presents a different error.
TS2604: JSX element type 'Reaptcha' does not have any construct or call signatures.
And this latter error, I'm quite stuck on. Any assistance getting past this appreciated.
The minimal case of where the component is used is:
public render() {
const Fragment = React.Fragment;
return (
<Fragment>
<Reaptcha
ref={(e: any) => (this.captcha = e)}
sitekey="MY KEY"
onVerify={this.onVerify}
/>
</Fragment>
);
}
The Reaptcha readme shows the import as:
import Reaptcha from 'reaptcha';
I tried this style of import with your original declaration and TypeScript was happy but the import didn't work at runtime. When I enabled the --esModuleInterop TypeScript option, then everything worked. Try using this option if you aren't already.
You could just use:
declare module "reaptcha";

How to use hook_swiper_options_alter($node, $plugin_options) {} in Drupal-7

I have been searching for a few days to figure out how to change the options for the swiper (v 7.x-1.4) module in Drupal-7. The documentation is clear-as-mud explaining how the module expects this hook to be used. I'm looking for a simple code example on how to implement the following options from the swiper API:
autoplay
prevButton
nextButton
autoplayDisableOnInteraction
The only documentation reference I have been able to find is from the README.txt in the module:
...
You can also add, change and remove, any of API options of the Swipers,
just you need to implement a hook:
hook_swiper_options_alter($node, $plugin_options) {}
This way the module will handle pass these options to the script that
instantiates the swiper Plugin.
...
I'm fairly new to Drupal, but I am trying to learn. I have attempted to create a simple custom module to implement these options. I have called my module myCustom, created the /drupal/sites/all/modules/myCustom directory with the following files:
myCustom.info:
name = myCustom
description = customize swiper
package = me
version = 0.02
core = 7.x
files[] = myCustom.module
myCustom.module:
<?php
function myCustom_swiper_options_alter($node, $plugin_options)
{
$plugin_options += (
nextButton: '.swiper-button-next',
prevButton: '.swiper-button-prev',
paginationClickable: true,
autoplay: 2500,
autoplayDisableOnInteraction: true
);
return($node, $plugin_options);
}
I know I have multiple problems. Drupal refuses to enable my module as-is and I can not figure out why. I have checked the admin->reports->recent log messages report and found nothing relevant to at least help me troubleshoot.
Any ideas how I can fix this? Does anyone have a working example of code that I can copy and modify to get this hook working?
Thank you in advance for any assistance!
You may want to read through this documentation: Writing module .info files (Drupal 7.x).
Remove this line from your .info file: files[] = myCustom.module. Drupal will automatically read through the .module file.
As you defined a version in your .info file this may need your attention: Release naming conventions, but actually you can just leave that out as well, it's not mandatory.
Since you're using a hook from that swiper module, I recommend to set it as a dependency in your custom module's .info file as: dependencies[] = swiper to prevent unmet dependency errors.
Change the $plugin_options array to a php array & do not return anything:
<?php
function YOUR_MODULE_swiper_options_alter($node, &$plugin_options) {
$plugin_options += array(
'nextButton' => '.swiper-button-next',
'prevButton' => '.swiper-button-prev',
'paginationClickable' => true,
'autoplay' => 2500,
'autoplayDisableOnInteraction' => true,
);
}
Additionally: Try to refrain from using capitals in module names as per machine name (module dir name). If you take a look at other Drupal modules in /modules or sites/all/modules they're all lowercased. (You can leave the name in your .info file which also represents your module in the modules list as you have now.)

Resources