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

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/

Related

How to create a dynamic layout in react with fully configurable JSON data

I am writing a react application. A core requirement is that the application be completely dynamic and configurable, including choosing layouts, sections and fields, validation etc.
I have two UI. One is the config UI where the user can select the layout, sections, fields like what type of html component etc. Once this is saved, I get data as JSON where I need to draw the UI. This is my second UI. My concern is how do I structure the components to render the UI with the JSON data. The fields & sections will be same but the layout will be different based on what is been selected in the config UI. Below is the rough JSON schema.
{
title: "Test title",
layout: [
{
name: "layout-a"
},
sectionA: {
name: "breadcrumbs"
field: [
{
name: "test",
value: "test",
type: "text"
}
]
},
sectionB: {
name: "actions"
field: [
{
name: "Create",
value: "Create",
type: "button"
}
]
}
]
}
I was thinking of having a layout component which renders all the children from the JSON. Component looks like below
const Layout = ({ children }) => {
return (
<div>
<div className="container">
<div className="content">{children}</div>
</div>
</div>
);
};
and top level component where we read the config json and based on the layout render the component
<Layout>
{viewToShow === "layoutA" && <LayoutA data={config.sections} />}
{viewToShow === "layoutB" && <LayoutB data={config.sections} />}
</Layout>
My question is how do I construct the LayoutA, B or C component so that these sections and fields are rendered differently on the UI?
I think your question leaves a lot of unspecified points for us to offer you a proper solution. My advice is to investigate better what the project real needs are and its main goals, then lay out each piece (component) thoroughly checking what should be "configurable" and to which extent, before coming up with any implementation.
Taking your example "as is", my first thought is to wrap your App component into a Context provider, similar to what we'd do to manage themes.
export const layouts = {
layoutA: {
background: '#fff',
sectionWidth: '100%',
},
layoutB: {
background: '#000',
sectionWidth: '50%',
},
};
export const LayoutContext = React.createContext({
layout: layouts.layoutA, // default layout
toggleLayout: () => {},
})
You could then further populate the layouts object with metadata from a database. Supposing changes do not originate from the UI (think Webflow or Wix Editor), you could use a CMS to update the metadata and propagate the changes.
An example usage would be:
function LayoutTogglerButton() {
return (
<LayoutContext.Consumer>
{({ layout, toggleLayout }) => (
<button
onClick={toggleLayout}
style={{ backgroundColor: layout.background }}>
Toggle Layout
</button>
)}
</LayoutContext.Consumer>
)
}
Again, there are a lot of unspecified points on your request for us to be more specific. The request for "an application to be completely dynamic and configurable, including choosing layouts, sections and fields, validation etc" could mean many things.
Examples of more specific questions: How to create dynamic forms in React with functional components? How to create drag and drop dashboard widgets with React? How to live update/customise themes with styled-components?
Perhaps you could be more specific? Cheers
I am researching a possibility to do something similar. An off the bat approach would look somewhat like this: https://codesandbox.io/s/still-sun-cecudh?file=/src/App.js
Then of course, where this the layout object will be generated and where the parsing will take place will dependent on your use case. I am going with context for layout object generation and a dedicated component for object tree traversal.

Export Material UI Table in excel/csv format

In my web application I am using a Table from (material-ui/core/Table) is it possible to export it in excel or csv format? I cannot find anything from the documentation of material UI, except for using custom projects.
According to the Material-UI Table API, it doesn't offer that functionality.
I suggest you use Material-UI DataGrid to render tables as it offers the functionality of downloading the table as a csv file.
Here's the link to the DataGrid which has the functionality implemented using GridToolbar: https://material-ui.com/components/data-grid/export/
first lets import the library,
import { ExportCsv, ExportPdf } from '#material-table/exporters';
After Importing add the below thing to the material table options,
exportMenu: [{
label: 'Export PDF',
exportFunc: (cols, datas) => ExportPdf(cols, datas, 'myPdfFileName')
}, {
label: 'Export CSV',
exportFunc: (cols, datas) => ExportCsv(cols, datas, 'myCsvFileName')
}]

Is there a way to add new tab in WordPress Gutenberg editor

I am completely new to Gutenberg and I need to add a new tab in the setting section Please check this screenshot
I Have created some blocks for Gutenberg but no experience in this. I tried this code
import { TabPanel } from '#wordpress/components';
const onSelect = ( tabName ) => {
console.log( 'Selecting tab', tabName );
};
const MyTabPanel = () => (
<TabPanel className="my-tab-panel"
activeClass="active-tab"
onSelect={ onSelect }
tabs={ [
{
name: 'tab1',
title: 'Tab 1',
className: 'tab-one',
},
{
name: 'tab2',
title: 'Tab 2',
className: 'tab-two',
},
] }>
{
( tab ) => <p>{ tab.title }</p>
}
</TabPanel>
);
But didn't help me. Anyone here please help me.
Thanks in advance
In the screenshot you provided, the location you are attempting to add a tab to is the Settings Header
(gutenberg/packages/edit-post/src/components/sidebar/settings-header) for which there is not currently a slot in the Gutenberg API to extend from (although this could potentially be done, it's best to not interfere with core UI).
The prefered method to add to the Admin UI is to use an provided SlotFill for custom content, currently there are:
PluginBlockSettingsMenuItem
PluginDocumentSettingPanel
PluginMoreMenuItem
PluginPostPublishPanel
PluginPostStatusInfo
PluginPrePublishPanel
PluginSidebar
PluginSidebarMoreMenuItem
The PluginSidebar slot is useful for adding custom content that is specific for your plugins/blocks purpose. The main point to consider is whether the content you wish to add applies just to your block, the post/page as a whole or is some other 'global' setting to do with a plugin.
If your content applies to the whole post/page, the PluginPostStatusInfo slot may be a good location to add to. You could also add your own Panel that appears underneath the "Document" tab.
If the content is block-specific, then you can use the to add custom controls underneath "Block" tab that contextually show when your block is selected. This would also be a good place for meta field values that are specific to the block or custom controls for colors/display options related to your block.
The official WordPress Gutenberg documentation also has a tutorial on Block Controls: Block Toolbar and Settings Sidebar which walks through some common scenarios for adding your own settings in Blocks.

Best way to layout application?

Edit: Fixed JSFiddle Link
So i've been playing with Backbone and Marionette since a couple of weeks. I did some courses on Udemy, read the docs for both Backbone and Marionette. I can grasp most of the logic but somehow my mind can't wrap itself around the best way to approach a SPA I am trying to create.
API
So I have a rest api that returns some data:
http://localhost:3000/index
returns the following:
[
{
"id": 1,
"magazineTitle": "Title",
"magazineEditie": "Date",
"indexTitle": "Index Title",
"indexSubtitle": "Index Subtitle",
"mediaType": "image", //can also be "video"
"mainMedia": "https://source.unsplash.com/B0iF3I4bLBQ"
}
]
What I want
I want to be able to use this response and populate it over 2 seperate views.
one view will use the data to create a navigation bar
the other view will use it to create a hero header
What I can't seem to understand
Somehow I can't wrap my head around how this would be set up without making this 'illogical'
I feel like loading 2 views with the same model inside my Marionette.Application doesn't make any sense? Or the fact that I fetch my Collections and/or Models there...
I need some help clearing up some Layout issues and best practices I guess.
My code
Besides the fact that I get the data from a localhost url and I have my app setup with webpack this is more or less the code that I am using:
JSFiddle Demo
I have figured out what I needed to do. Based on the documentation (which was kind of confusing me) I figured out a way to render two views with it's needed data.
I was using a CollectionView to read a single data point (1 model) I somehow couldn't figure out a way to immediately get a single Model.
So far the model I had to do:
index.model.js
const IndexModel = Backbone.Model.extend({
urlRoot: "http://localhost:3000/index",
default: {
id: 1,
magazineTitle: "Mag Title",
magazineEditie: "Date",
indexTitle: "Title",
indexSubtitle: "Subtitle",
mediaType: "image",
mainMedia: "http://placehold.it/1900x800/",
},
});
The urlRoot argument here is what I need to do the exact call.
Then I was still confused how to structure my app but I ultimately used Regions and Marionette.View to setup the application.
App.js
export default Marionette.Application.extend({
region: "#content",
onBeforeStart() {
const router = new Router();
},
onStart() {
this.showView(new AppView());
},
});
app.view.js
const AppView = Marionette.View.extend({
tagName: "main",
id: "app",
template: template,
regions: {
navigationRegion: "#main-navigation",
appRegion: "#main-region",
pagesRegion: "#pages-region",
},
initialize() {
this.headerData = new IndexModel({ id: 1 });
this.pagesData = new PagesCollection();
},
onRender() {
this.showChildView("appRegion", new HeroView({ model: this.headerData, }));
this.showChildView("pagesRegion", new PagesView({ collection: this.pagesData, }));
},
});
I had to create a wrapping AppView that utilises regions to specify where child views should render.

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