Auto generated key in Ant Design Upload react component causes snapshot test to fail - reactjs

I am using the Upload ant design component, and its working well except it generates a file input with an auto generated key. Every time I run the tests, a new key is generated, so the snapshot don't match and my test fails.
Setting the key on the Upload doesn't affect the input key, so I have no evident way to mock this. I also tried using the new property matchers, but all the examples I found were very simple, using one simple object, couldn't figure out how to use with a wrapper containing many nested react components.
I couldn't find any documentation on how to deal with ant design auto generated keys... Any help or pointing in the right direction would be very much appreciated!!

This works for me using Enzyme and Jest with snapshot testing:
describe('<Uploader />', () => {
test('it renders', () => {
const uploaderProps = {
accept: 'application/pdf,image/*',
action: 'https://file-service.example.com/v1/upload',
defaultFileList: [
{
uid: '1',
name: 'under-construction.gif',
status: 'done',
url: 'https://media.giphy.com/media/EIiJp9cQ3GeEU/giphy.gif',
thumbUrl: 'https://media.giphy.com/media/EIiJp9cQ3GeEU/giphy.gif',
},
],
multiple: true,
name: 'file-input',
onChange: jest.fn(),
};
const uploader = mount(<Uploader {...uploaderProps} />);
expect(uploader.render()).toMatchSnapshot();
});
});
Note the use of .render() to generate the snapshot without the key.

Related

How to add 3rd party editor to Wagtail 2.2+?

We have been using Froala editor within Wagtail for years, and even though Draftail is nice, the end user wants to continue to use Frola, especially as a license fee has been paid for it, and it offers extra functionality that they use.
I have used the following which works great for any version of Wagtail prior to version 2.2:
https://github.com/jaydensmith/wagtailfroala/pull/5/commits/d64d00831375489cfacefa7af697a9e76fb7f175
However in Wagtail version 2.2 this has changed:
https://docs.wagtail.org/en/stable/releases/2.2.html#javascript-templates-in-modal-workflows-are-deprecated, this causes selecting images within Froala to no longer work correctly. You get to pick the image, but it fails to move onto the 2nd dialog to select alignment and then click insert.
It looks like wagtailfroala/static/froala/js/froala.js needs to be changed to add onload to the ModalWorkflow.
$.FE.RegisterCommand('insertImage', {
title: 'Insert Image',
undo: false,
focus: true,
refreshAfterCallback: false,
popup: true,
callback: function () {
var editor = this;
return ModalWorkflow({
url: window.chooserUrls.imageChooser + '?select_format=true',
responses: {
imageChosen: function(imageData) {
editor.edit.off();
var $img = $(imageData.html);
$img.on('load', function() {
_loadedCallback(editor, $(this));
});
// Make sure we have focus.
// Call the event.
editor.edit.on();
editor.events.focus(true);
editor.selection.restore();
editor.undo.saveStep();
// Insert marker and then replace it with the image.
if (editor.opts.imageSplitHTML) {
editor.markers.split();
} else {
editor.markers.insert();
}
var $marker = editor.$el.find('.fr-marker');
$marker.replaceWith($img);
editor.html.wrap();
editor.selection.clear();
editor.events.trigger('contentChanged');
editor.undo.saveStep();
editor.events.trigger('image.inserted', [$img]);
}
}
});
},
plugin: 'image'
});
But what needs to be added to make it work? I can't find any documentation or examples on how do to this? Please help or point me in the direction of the documentation for how this should be done.
Thank you so much in advance.

How to create custom netlifyCMS widget that can handle a query?

So I want to have a select widget where the options are based on some dynamic data that I have to query for. However, it seems that custom widgets break when importing useStaticQuery.
The below gives me "no control widget in the CMS". It works fine without the useStaticQuery import.
import React from 'react';
import { useStaticQuery, graphql } from "gatsby"
export class CustomControl extends React.Component {
render() {
return (
<div>
...
</div>
)
};
}
export const CustomPreview = (props) => {
return (
<div></div>
);
}
Generally, is there a best way/practice to go about creating a custom widget that can handle dynamic values?
Update:
I have tried the relation widget with no luck. I have existing data in a collection but can't seem to access it from the widget. Does someone have a working version of one I can go off of?
The collection that is meant to be the "data":
- label: Team
name: team
folder: 'src/pages/team'
create: true
fields:
- {label: 'Name', name: 'name', widget: string}
and the relation widget:
- label: 'Relation widget'
name: 'relationWidget'
widget: 'relation'
collection: 'team'
searchFields: ['name']
valueField: 'name'
displayFields: ['name']
With the NetlifyCMS structure, the best way to access other data is through the relation widget rather than a query.
However, in order to actually see this working, the site needs to be live. You can't locally mock data. Meaning, you can't go to localhost:8000/admin and see the relation widget pull anything.
(This is kind of a hassle because you have to authenticate a user as well and rebuild the whole site just to see the one change. It seems like you should be able to either query or just run the CMS locally and mess around with it that way)
Update
In order to pass multiple values through a relation widget:
value_field: "{{value1}}-{{value2}}"
create a proxy server to run the CMS locally. This is still in beta at the moment for netlifyCMS but seems to work well.
https://www.netlifycms.org/docs/beta-features/

Component definition - Missing display name error

I'm trying to build a custom panel option editor in web app called Grafana, but am running into an error I suspect is no more than a React syntax issue.
195:15 error Component definition is missing display name react/display-name
export const optionsBuilder = (builder: PanelOptionsEditorBuilder<SVGOptions>) => {
return builder
.addBooleanSwitch({
category: ['SVG Document'],
path: 'svgAutoComplete',
name: 'Enable SVG AutoComplete',
description: 'Enable editor autocompletion, optional as it can be buggy on large documents',
})
.addCustomEditor({
category: ['SVG Document'],
path: 'svgSource',
name: 'SVG Document',
description: `Editor for SVG Document, while small tweaks can be made here, we recommend using a dedicated
Graphical SVG Editor and simply pasting the resulting XML here`,
id: 'svgSource',
defaultValue: props_defaults.svgNode,
editor: (props) => {
const grafanaTheme = config.theme.name;
return (
<MonacoEditor
language="xml"
theme={grafanaTheme === 'Grafana Light' ? 'vs-light' : 'vs-dark'}
value={props.value}
onChange={props.onChange}
/>
);
},
})
};
To use a custom panel option editor, use the addCustomEditor on the OptionsUIBuilder object in your module.ts file. Configure the editor to use by setting the editor property to the SimpleEditor component.
The tutorial in the Grafana Docs explains more about what I'm doing, but I believe the issue is just with the arrow function I use at line 195.
Is there a different way I should be retrieving my editor property?

Multiple validations in FineUploader

I'm using FineUploader in my React app to upload files to Azure Blob Storage. I'm currently using validation to make sure a user can only upload one file. I now want to add two more validations:
I want to allow only JPG and PNG files
I also want to make sure that the pixel size of the file user can upload must be at least 300x300 pixels. In other words, I want to impose both width and height requirements
How do I add these validations? Do I need a validation property inside options for each requirement or do they go into the existing one? My current validation looks like this:
// Omitted for brevity
constructor(props) {
super(props);
this.uploader = new FineUploaderAzure({
options: {
cors: {
expected: true,
sendCredentials: false
},
signature: {
endpoint: "some-url.com"
},
request: {
endpoint: "my-container-url"
},
validation: {
multiple: false
}
},
callbacks: {
onError: function (id, name, errorReason, xhrOrXdr) {
}
}
})
}
Assume react/fine-uploader don't change it, by Documentation, you should do it like this.
options: {
multiple: false,
cors: {
expected: true,
sendCredentials: false
},
signature: {
endpoint: "some-url.com"
},
request: {
endpoint: "my-container-url"
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'png'],
image:{
minHeight: 300,
minWidth: 300
}
}
}
PS. I didn't use fineuploader in react, so please see if this is working as well in react.
Answer to update
In case you want to have your own validation, first of all you should not use onError.
onError is invoked if and only if error occurs. You validate the image size, though the image doesn't match your criteria, it is NOT error.
Then you have two choice to do your validation, that depends on what effect you want. First you need to know the flow of events. It is:
onSubmit -> validation -> onValidate
The work (check is image or not) you most likely want can be done on both three places.
If you want it in the validation, then the answer I provided already made it (allowedExtensions: ['jpeg', 'jpg', 'png']). But let's say you want to make some customization of alert or other checking, you need to do it in onValidate.
callbacks:{
onValidate: function(data, button_container) {
console.log(data); //data.name && data.size
return false;
}
}
But please be reminded that in onValidate, you can only get back file name and file size, no other information.
If you want to also check the width & height, you probably need to take a look on this SO question (give OP a upvote if you find this is what you want.)

How to post many-to-many relational data to a restful api from AngularJS?

I have a Theme Management module in my web application. I'รถ using SequelizeJS in server side.
Models are:
module.exports = function(sequelize, DataTypes) {
var Theme = sequelize.define('Theme', {
name: DataTypes.STRING,
description: DataTypes.STRING
}, {
associate: function(models) {
Theme.belongsToMany(models.Option, { through: models.ThemeOptions })
},
tableName: 'themes'
});
return Theme;
};
module.exports = function(sequelize, DataTypes) {
var ThemeOptions = sequelize.define('ThemeOptions', {
}, {
tableName: 'theme_options'
});
return ThemeOptions;
};
module.exports = function(sequelize, DataTypes) {
var Option = sequelize.define('Option', {
key: DataTypes.STRING,
value: DataTypes.STRING
}, {
associate: function(models) {
Option.belongsToMany(models.Theme, { through: models.ThemeOptions })
},
tableName: 'options',
timestamps: false
});
return Option;
};
In /#/themes/create state, I want to create a theme with some options, like color codes.
I am creating a theme with
$http.post('/themes', themeData)
then with it's it, I am creating options. Finally I should post many-to-many data to theme options. So for a theme that has 10 options, I am posting 21 times.
What is the best way to post many-to-many data to a REST server?
Don't really know how you handle your routing or backend. Supposing you're using express and your options are previously created, I'd recommend creating a new post route for handling each ThemeOptions
app.post('/themeOptions', { ThemeId: 1, OptionId:2 });
and use that info to create a ThemeOptions instance to join a Theme with certain Option.
This could reduce your post quantity to the half + 1 (one for the Theme and one for each ThemeOption).
Another solution is to maybe manage an array of ThemeOptions and use ThemeOptions#bulkCreate to create them at once, using only 2 posts (one for the Theme and one for all the ThemeOptions.
Would be something like this:
app.post('/themeOptions', {
options: [{
ThemeId: 1,
OptionId:2
}, {
ThemeId: 1,
OptionId:3
}
// and so on...
});
Each of these solutions could involve more logic to manage each front end request, but could increase front end behavior as well.
A final (and more complex at the backend) solution would be to send a unique post sending both, the Theme and the Options array, and create all the ThemeOptions after creating the Theme
// frontend
app.post('/theme', {
theme: {
name: 'John',
description: 'Doe'
},
options: [2, 3 /* and so on ... */]
});
// backend
Theme.create(req.body.theme).on('success', function (theme) {
var options = req.body.options.map(function (option) {
return {
ThemeId: theme.id,
OptionId: option
};
});
ThemeOptions.bulkCreate(options);
})
In the Symfony2 world, there is a bundle, SonataAdminBundle, that generates admin interfaces. With the entity classes (here, Option and Theme), it generates all the pages, listing, creating and editing theses entities. It generates forms that handles many to many relationship. Here how it manages that :
The user consults the creation/edition form of the entity of any side of the ManyToMany relation. In the form, where it have to display the many to many association, it displays a <select>, with Select2 for instance, which is a <select> with some jQuery. Each element of the list is linked with the corresponding ID in the database, something like <option value="13456">Option #3</select>. For a many to many relationship, we can select multiple fields at the time. Internally, it builds an array of Option IDs with the <select>.
If we want to add a inverse entity on the fly (here, the inverse entity is Option, I think ...), there is a button that open ups an Option creation form, and once the new Option is added, it adds the newly created option in the <select>, so the user can add it in the form immediately.
Then, it sends the array of Option ID's built with the form.
I think this strategy could fit your needs.

Resources