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?
Related
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.
I've created a custom dialog with a dropzone in order to upload files and images to our server. By default the dropzone renders with 'Drop an image here' / 'Browse for an image'.
As part of the tinymce editor options file_picker_type allows setting of the default dropzone and I wondered how to access these options for a stand-alone dropzone?
My tinymce options include:
tinymce.init({
file_picker_types: 'file image'
});
My upload dialog config looks something like this:
const upload_config = {
title: 'Upload files',
size: 'medium',
body: {
type: 'panel',
{
type: 'dropzone',
name: 'file_drop',
types: 'file image' //// <----- something like this???
}
]
},
buttons: [
{
type: 'cancel',
name: 'closeButton',
text: 'Cancel',
},
{
type: 'submit',
name: 'submitButton',
text: 'Upload',
buttonType: 'primary'
}
],
onSubmit: (api) => {
// handle upload here
}
};
Anybody managed to fathom this? The tinymce docs are in no way comprehensive as they simply say "A dropzone is a composite component that catches drag and drops items or lets the user browse that can send a list of files for processing and receive the result." but give no details on how to actually do that with a stand alone dropzone.
I've managed to make it all work, but this is the last hurdle.
Thanks in advance
Are you trying to override the default image/file upload dialog with the custom one? I'm asking because I see the file_picker_types config option. To do so, you will need to use the file_picker_callback.
If it's something else and you are not trying to override the default dialog, use this interactive example as a base. You will need to:
a) Register the custom button with a setup function:
setup: function (editor) {
editor.ui.registry.addButton('custom-upload', {
icon: 'upload',
onAction: function () {
editor.windowManager.open(upload_config)
}
})
},
b) Add this button to the toolbar via toolbar: 'custom-upload'. This way, pressing the button will open the dialog based on the upload_config variable.
c) You've made a mistake in the panel config. I guess, you forgot to create the items array at the beginning. Instead of
type: 'panel',
{
type: 'dropzone',
name: 'file_drop',
types: 'file image' //// <----- something like this???
}
]
There should be:
type: 'panel',
items: [
{
type: 'dropzone',
name: 'file_drop',
label: 'Dropzone',
}
]
I've created this fiddle as a quick demo: https://fiddle.tiny.cloud/3iiaab
P.S. The dropzone component does not support file types. But you can check the file types after they are uploaded.
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/
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.
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.