I am looking for a tutor for module development

I took a break from the GUI to work on the actual date calculating logic. I started this in a separate IntelliJ Project because I didn’t know how else to test it without re-building it and then reimporting it to Ignition every time.

I shortly realized I would really like to leverage the prebuilt system.date.* functions instead of manually doing date arithmetic (is there anything more disgusting?).

Is there a way to do that where I could run? Right now the code I’m testing looks like

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

public class DateCalculator {
    public DateCalculator(){}

    private Date calculateEndDate(String interval, Date curDate) {
        Date endDate = new Date();
        Calendar cal = Calendar.getInstance();
        switch(interval){
            case "All":
                cal.add(Calendar.DATE, 30);
                endDate = cal.getTime();
                break;
            case "Today":
                LocalTime midnight = LocalTime.MIDNIGHT;
                LocalDate today = LocalDate.now(ZoneId.of("US/Eastern"));
                LocalDateTime todayMidnight = LocalDateTime.of(today, midnight);
                LocalDateTime yesterdayMidnight = todayMidnight.plusDays(1);
                //This is how you convert a localdatetime to Date
                endDate = Date.from(yesterdayMidnight.atZone(ZoneId.of("US/Eastern")).toInstant());
                break;
            case "This Week":
                break;
        }
        return endDate;
    }

    private Date calculateStartDate(String interval, Date curDate) {
        Date startDate = new Date();
        switch(interval) {
            case "All":
                //Start of unix time
                startDate = new Date(0L);
                break;
            case "Today":
                LocalTime midnight = LocalTime.MIDNIGHT;
                LocalDate today = LocalDate.now(ZoneId.of("US/Eastern"));
                LocalDateTime todayMidnight = LocalDateTime.of(today, midnight);
                //This is how you convert a localdatetime to Date
                startDate = Date.from(todayMidnight.atZone(ZoneId.of("US/Eastern")).toInstant());
                break;
            case "This Week":
                break;
        }
        return startDate;
    }

    public ArrayList<Date> calculateEndPoints(String interval, Date curDate){
        ArrayList<Date> returnDates = new ArrayList();
        Date endDate = calculateEndDate(interval, curDate);
        Date startDate = calculateStartDate(interval, curDate);
        returnDates.add(startDate);
        returnDates.add(endDate);
        return returnDates;
    }

    public void printEndPoints(ArrayList<Date> endpoints, String option) {
        System.out.println("Endpoints for " + option + " selection.");
        System.out.println("Start date is " + endpoints.get(0));
        System.out.println("End date is " + endpoints.get(1));
    }

    public static void main(String[] args){
        DateCalculator dc = new DateCalculator();
        Date curDate = new Date();
        ArrayList<Date> AllEndPoints = dc.calculateEndPoints("All", curDate);
        ArrayList<Date> TodayEndPoints = dc.calculateEndPoints("Today", curDate);
        dc.printEndPoints(AllEndPoints, "All");
        dc.printEndPoints(TodayEndPoints, "Today");
    }
}

I realized quickly its becoming mess of different date time libraries right now and I am not using timzones on everything which I think the system.date does do (correct me if I am wrong) - a large part of why I would like to use them. Also a big advantage would be to use the simpler system.date.add* and system.date.get* functions would help me do the more complicated calculations much more easily. What would be the easiest/ most appropriate way to be able to use those functions in my testing of this DateCalculator? FWIW the original project I made from the vision-component-archetype so I don’t know if that means the scripting functions are there or not so I don’t know if bringing this over to that would work automatically.

Most (all?) of system.date's functionality is in static functions in com.inductiveautomation.ignition.common.script.builtin.DateUtilities, which, being in common, should be available to you in any scope.

The underlying functionality is driven by either Calendar or, for the math, an imported joda-time dependency.
For consistency with our code, it’s probably reasonable to use our libs.
In your maven or gradle setup, with common marked as a dependency appropriately, you should be able to run our code in your own project without launching a whole gateway (in this case). Considering looking into a unit testing framework such as JUnit.

As an aside - our use of Date is mostly historical baggage at this point. In a perfect world we’d have migrated away from it once we had the chance. The gold standard for date/time operations is the java.time package, e.g LocalDateTime; the problem is Date is a “lossy” conversion from any of these objects so you usually have to jump through some hoops to get from them to a Date.

1 Like

Over the weekend I tried reading up more on the build tools and after a while I quickly came around to thinking Gradle was much easier to manage and handle. I just have three probably simple questions as I want to refactor what I currently have within a Gradle structured project. The example I learned from gave me a setup like this -

image

Question 1:
I know in the maven setup I have a build/client/designer folder. Are all three still necessary, and how do they fit in here? Would they all go under my main?

Question 2:
Related to one - in my simple app I have

jar {
    manifest {
        attributes 'Main-Class': 'com.primarysys.MyApplication'
    }
}

to build my class. What would in my manifest to properly build the componeont?

Last Question:
I was trying to figure out how to add the dependencies. My repository is mavenCentral(). I was looking com.inductiveautomation.ignition.common.script.builtin.DateUtilities as well com.inductiveautomation.vision and com.inductiveautomation.factorypmi as this has the date functions and Ignition components I am currently using in my maven version.

When I am trying to add the factorypmi to get the datetime selector component for instance, I do a Alt+Insert in my dependency, do a Maven Artificat Search, and get the following -

or by class

What is the “right” way to find the Ignition packages I need and add them as a dependency? Thanks for all the continued support on this.

Just like Maven, you would need three (as Gradle calls them, not sure on Maven) subprojects, because you’re still ultimately producing three different artifacts. The structure is broadly the same - a folder at the root level, with a separate build.gradle inside it that tells Gradle how to build this project (just like an inner pom.xml).

Ignition doesn’t care about the manifest (at some point, maybe it might be nice to change this, so you don’t have to separately specify the hook class in the module info, but it’s not a big gain, really).

You’ll need to manually declare additional maven repositories in your build.gradle files.
See the Perspective example project for reference:

This is another one of those somewhat confusing things - you’re using Gradle, but downloading artifacts from our server that implements the Maven dependency resolution spec.

Once you have those repositories selected, you would add the appropriate dependencies to your individual subprojects, and they should resolve (and be auto-completeable) in your actual source files.

Coming back after a while off of this.

I got it mostly working. I wanted to refactor a couple things though, the PMIComboBox and the JComboBox do look different enough where I would rather use the PMIComboBox and therefore need to set the data of it with a Dataset like this

//I know this is wrong
private final String[][] dateSelectionOptions = {{0, "All"}, {1,"Today"}, {2,"This Week"}, {4,"This Month"}, {5,"This Year"}, {6,"Last Week"}, {7,"Last Month"}, {8,"Last Year"}, {9,"Last 3 Months"}, {10,"Last 6 Months"}, {11,"Last 12 Months"}, {12,"Two Week Period"}, {13,"Post Sept. 2014"}};
...
dateSelections = new PMIComboBox();
//To do - figure out making the basic dataset - don't think second argument is right and third definitely isn't
BasicDataset ds = new BasicDataset({"Value","Label"}, {Integer, String}, dateSelectionOptions);
dateSelections.setDataDirectly(ds);

Right now I am struggling to make a basic dataset.

Upon checking out the documentation It seems like I am supposed to feed a 2d list of objects as the third argument. Is there some object that I should be using? I tried making something like this

public class DatasetRow {
    private final String Label;
    private final int Value;

    public DatasetRow(String Label, int Value) {
        this.Label = Label;
        this.Value = Value;
    }
}

But this actually takes care of both columns, but that isn’t quite right.

I think it should be something like

private final SomeColumnDataClass datasetData = {{new SomeColumnDataClass(0?), new SomeColumnDataClass("All"?)}, new SomeColumnDataClass(1?), new SomeColumnDataClass("This week"?)}}

? indicating I don’t know what this classes constructor should look like but trying to convey my data.

BasicDataset data is columnwise.

Is there a more appropriate instance of Dataset other than BasicDataset I should be using for this case, like an ArrayDataset?

I recommend using the DatasetBuilder tool. It yields a BasicDataset, but constructed row-wise.

1 Like

Worked on this a bit this weekend.

I have the right framework set out now so I just have to find some time do write up the date calculating functions.

I am noticing two issues I want to try to resolve before proceeding.

  1. Dragging and dropping my component (or putting the desginer in play mode) throws a Null Pointer Exception immediately. I thought this was because my onActionPerformed needed a value, so I tried presetting my dropdown to my first option, but that didn’t seem to work.

  2. When I change my dropdown, it seems to trigger things twice (also get the that Couldn’t instantiate type editor shows occasionally)-

Here is my full code currently - if you see anything else that would is generally problematic or bad practice let me know as well -

package org.example.client;

import com.inductiveautomation.factorypmi.application.components.PMIDateTimePopupSelector;
import com.inductiveautomation.factorypmi.application.components.PMIComboBox;
import com.inductiveautomation.ignition.common.Dataset;
import com.inductiveautomation.ignition.common.script.builtin.DateUtilities;
import com.inductiveautomation.ignition.common.util.DatasetBuilder;
import com.inductiveautomation.vision.api.client.components.model.AbstractVisionPanel;
import net.miginfocom.swing.MigLayout;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;

//Should this be extending AbstractVisionPanel instead?
public class DateSelectorComponent extends AbstractVisionPanel {

    private final JPanel container;
    private final JLabel dropdownLabel;
    private final PMIComboBox dateSelections;
    private final JLabel startDateLabel;
    private final JLabel toLabel;
    private final PMIDateTimePopupSelector startDate;
    private final PMIDateTimePopupSelector endDate;
    //TODO list of ints and strings instead of string and strings
    private final Dataset dataSelectionOptions;
    private final String[] dateSelectionOptionsStr = {"All", "Today", "This Week", "This Month", "This Year", "Last Week", "Last Month", "Last Year", "Last 3 Months", "Last 6 Months", "Last 12 Months", "Two Week Period", "Post Sept. 2014"};
    private final String formattedDateString = "MM/dd/yyyy";
    private final Date currentDate;

    public DateSelectorComponent() {
        //Leftover from archetype example.  setPreferredsize seems to do stuff, but unsure what else is appropriate to go here over onStartup.
        setFont(new Font("Dialog", Font.PLAIN, 16));
//        setPreferredSize(new Dimension(250, 100));
        container = new JPanel(new MigLayout("wrap 4"));
        //Dropdown row
        dropdownLabel = new JLabel("Date:");
        dateSelections = new PMIComboBox();
        DatasetBuilder db = new DatasetBuilder().newBuilder();
        db.colNames("Value", "Label").colTypes(Integer.class, String.class);
        for (int i=0; i < dateSelectionOptionsStr.length; i++) {
            db.addRow(i, dateSelectionOptionsStr[i]);
        };
        dataSelectionOptions = db.build();
        dateSelections.setData(dataSelectionOptions);
        dateSelections.setSelectedLabel("All");
        container.add(dropdownLabel);
        container.add(dateSelections, "span 3");
        //Datetime row
        startDateLabel = new JLabel("Start Date:");
        startDate = new PMIDateTimePopupSelector();
        startDate.setFormat(formattedDateString);
        toLabel = new JLabel("to");
        endDate = new PMIDateTimePopupSelector();
        endDate.setFormat(formattedDateString);
        container.add(startDateLabel);
        container.add(startDate);
        container.add(toLabel);
        container.add(endDate);
        currentDate = new Date();

        //Is this necessary?
        container.setVisible(true);
        this.add(container);
        this.setVisible(true);
        dateSelections.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String currentSelection = String.valueOf(dateSelections.getItemAt(dateSelections.getSelectedIndex()));
                System.out.println("Selected " + currentSelection);
                calculateDates(currentSelection);
            }
        });
    }

    private void calculateDates(@NotNull String dateSelection) {
        switch(dateSelection){
            case "All":
                calculateAllTime();
                break;
            case "Today":
                calculateToday();
                break;
            case "This Week":
                calculateThisWeek();
                break;
        }
    }

    private void calculateToday() {
        DateUtilities du = new DateUtilities();
        Date now = du.now();
        Date lastMidnight = du.midnight(now);
        Date tomorrowMidnight = du.midnight(du.addDays(now, 1));
        startDate.setDate(lastMidnight);
        endDate.setDate(tomorrowMidnight);
    }

    private void calculateAllTime() {
        startDate.setDate(new Date(0L));
        DateUtilities du = new DateUtilities();
        Date now = du.now();
        endDate.setDate(now);
    }

    private void calculateThisWeek() {
        DateUtilities du = new DateUtilities();
//        Date beginningOfWeek = du.midnight(du.addDays(du.now(),));
        //TODO Figure out getting start to end of week - think I need to use Joda directly
    }

    protected void onStartup() {

    }

    protected void onShutdown() {

    }
}

What line issues the NPE?

Consider initializing currentDate outside the constructor.

I wouldn’t bother with container. I’d just add its contents to this.

Got rid of the container and just added to this and the functionality remains the same and simplified the code a bit so that is good.

Just saw I had currentDate inside my constructor but never even use it so I have commented that out - I do all date calculations when the dropdown changes.

Here is the newly updated code

package org.example.client;

import com.inductiveautomation.factorypmi.application.components.PMIDateTimePopupSelector;
import com.inductiveautomation.factorypmi.application.components.PMIComboBox;
import com.inductiveautomation.ignition.common.Dataset;
import com.inductiveautomation.ignition.common.script.builtin.DateUtilities;
import com.inductiveautomation.ignition.common.util.DatasetBuilder;
import com.inductiveautomation.vision.api.client.components.model.AbstractVisionPanel;
import net.miginfocom.swing.MigLayout;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;

//Should this be extending AbstractVisionPanel instead?
public class DateSelectorComponent extends AbstractVisionPanel {

//    private final JPanel container;
    private final JLabel dropdownLabel;
    private final PMIComboBox dateSelections;
    private final JLabel startDateLabel;
    private final JLabel toLabel;
    private final PMIDateTimePopupSelector startDate;
    private final PMIDateTimePopupSelector endDate;
    //TODO list of ints and strings instead of string and strings
    private final Dataset dataSelectionOptions;
    private final String[] dateSelectionOptionsStr = {"All", "Today", "This Week", "This Month", "This Year", "Last Week", "Last Month", "Last Year", "Last 3 Months", "Last 6 Months", "Last 12 Months", "Two Week Period", "Post Sept. 2014"};
    private final String formattedDateString = "MM/dd/yyyy";

    public DateSelectorComponent() {
        //Leftover from archetype example.  setPreferredSize seems to do stuff, but unsure what else is appropriate to go here over onStartup.
        setFont(new Font("Dialog", Font.PLAIN, 16));
        //Dropdown row
        dropdownLabel = new JLabel("Date:");
        this.setLayout(new MigLayout("wrap 4"));
        dateSelections = new PMIComboBox();
        DatasetBuilder db = new DatasetBuilder().newBuilder();
        db.colNames("Value", "Label").colTypes(Integer.class, String.class);
        for (int i=0; i < dateSelectionOptionsStr.length; i++) {
            db.addRow(i, dateSelectionOptionsStr[i]);
        };
        dataSelectionOptions = db.build();
        dateSelections.setData(dataSelectionOptions);
        dateSelections.setSelectedLabel("All");
        this.add(dropdownLabel);
        this.add(dateSelections, "span 3");
        //Datetime row
        startDateLabel = new JLabel("Start Date:");
        startDate = new PMIDateTimePopupSelector();
        startDate.setFormat(formattedDateString);
        toLabel = new JLabel("to");
        endDate = new PMIDateTimePopupSelector();
        endDate.setFormat(formattedDateString);
        this.add(startDateLabel);
        this.add(startDate);
        this.add(toLabel);
        this.add(endDate);

        //Is this necessary?
        this.setVisible(true);
        dateSelections.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String currentSelection = String.valueOf(dateSelections.getItemAt(dateSelections.getSelectedIndex()));
                System.out.println("Selected " + currentSelection);
                calculateDates(currentSelection);
            }
        });
    }

    private void calculateDates(@NotNull String dateSelection) {
        switch(dateSelection){
            case "All":
                calculateAllTime();
                break;
            case "Today":
                calculateToday();
                break;
            case "This Week":
                calculateThisWeek();
                break;
        }
    }

    private void calculateToday() {
        DateUtilities du = new DateUtilities();
        Date now = du.now();
        Date lastMidnight = du.midnight(now);
        Date tomorrowMidnight = du.midnight(du.addDays(now, 1));
        startDate.setDate(lastMidnight);
        endDate.setDate(tomorrowMidnight);
    }

    private void calculateAllTime() {
        startDate.setDate(new Date(0L));
        DateUtilities du = new DateUtilities();
        Date now = du.now();
        endDate.setDate(now);
    }

    private void calculateThisWeek() {
        DateUtilities du = new DateUtilities();
//        Date beginningOfWeek = du.midnight(du.addDays(du.now(),));
        //TODO Figure out getting start to end of week - think I need to use Joda directly
    }

    protected void onStartup() {

    }

    protected void onShutdown() {

    }
}

And here is the NPE stack from the designer console - I can’t figure out line number it is referring to.

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
	at com.inductiveautomation.factorypmi.designer.property.editors.bb.SwingEnumEditor.setValue(SwingEnumEditor.java:79)
	at com.inductiveautomation.factorypmi.designer.property.editors.IntegerEditor.setValue(IntegerEditor.java:85)
	at com.inductiveautomation.factorypmi.designer.property.propertytable.PropertyValueEditor.getTableCellEditorComponent(PropertyValueEditor.java:154)
	at com.inductiveautomation.factorypmi.designer.property.propertytable.PropertyValueEditor.getTableCellRendererComponent(PropertyValueEditor.java:91)
	at com.jidesoft.grid.JideTable.a(Unknown Source)
	at com.jidesoft.grid.CellStyleTable.a(Unknown Source)
	at com.jidesoft.grid.JideTable.b(Unknown Source)
	at com.jidesoft.grid.CellSpanTable.calculateRowHeight(Unknown Source)
	at com.jidesoft.grid.JideTable.c(Unknown Source)
	at com.jidesoft.grid.JideTable.getCellRect(Unknown Source)
	at com.jidesoft.grid.CellSpanTable.getCellRect(Unknown Source)
	at java.desktop/javax.swing.plaf.basic.BasicTableUI.createTableSize(Unknown Source)
	at java.desktop/javax.swing.plaf.basic.BasicTableUI.getPreferredSize(Unknown Source)
	at java.desktop/javax.swing.JComponent.getPreferredSize(Unknown Source)
	at java.desktop/javax.swing.ScrollPaneLayout.layoutContainer(Unknown Source)
	at java.desktop/java.awt.Container.layout(Unknown Source)
	at java.desktop/java.awt.Container.doLayout(Unknown Source)
	at java.desktop/java.awt.Container.validateTree(Unknown Source)
	at java.desktop/java.awt.Container.validate(Unknown Source)
	at java.desktop/javax.swing.RepaintManager$3.run(Unknown Source)
	at java.desktop/javax.swing.RepaintManager$3.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/javax.swing.RepaintManager.validateInvalidComponents(Unknown Source)
	at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Couldn't instantiate type editor "com.sun.beans.editors.EnumEditor" : java.lang.InstantiationException: com.sun.beans.editors.EnumEditor
Couldn't instantiate type editor "com.sun.beans.editors.EnumEditor" : java.lang.InstantiationException: com.sun.beans.editors.EnumEditor

That NPE is coming from a table. I don’t see any table in your component, so I think that is something unrelated.

Do you have a beaninfo for your component? What’s it look like?

	at com.inductiveautomation.factorypmi.designer.property.editors.bb.SwingEnumEditor.setValue(SwingEnumEditor.java:79)
	at com.inductiveautomation.factorypmi.designer.property.editors.IntegerEditor.setValue(IntegerEditor.java:85)
	at com.inductiveautomation.factorypmi.designer.property.propertytable.PropertyValueEditor.getTableCellEditorComponent(PropertyValueEditor.java:154)
	at com.inductiveautomation.factorypmi.designer.property.propertytable.PropertyValueEditor.getTableCellRendererComponent(PropertyValueEditor.java:91)

I think you’ve exposed a raw enum on your component and the property editor isn’t happy with that. For serialization/deserialization (and probably other) reasons, Vision doesn’t really expose true enums - instead, a simple integer property is exposed, but masked as an enum by the editing UI.
E.G PMIComboBox’s ‘mode’ property is called an enum in the bean info but there’s no actual enum associated with it:

        addEnumProp("mode", "Dropdown Display Mode", "Changes the dropdown's display", CAT_APPEARANCE, new int[]{
            PMIComboBox.LIST, PMIComboBox.TABLE}, new String[]{"List", "Table"});

I think there’s something funky along these lines going on with your component.

2 Likes

Ah ok I thought that was a separate issue and would be what I wanted to tackle next. When I import my component the properties look like

image

Here’s my beans class file -

package org.example.designer.beaninfos;

import java.beans.IntrospectionException;

import org.example.client.DateSelectorComponent;
import com.inductiveautomation.factorypmi.designer.property.customizers.DynamicPropertyProviderCustomizer;
import com.inductiveautomation.factorypmi.designer.property.customizers.StyleCustomizer;
import com.inductiveautomation.vision.api.designer.beans.CommonBeanInfo;
import com.inductiveautomation.vision.api.designer.beans.VisionBeanDescriptor;

public class DateSelectorComponentBeanInfo extends CommonBeanInfo {

    public DateSelectorComponentBeanInfo() {
        super(DateSelectorComponent.class,
                DynamicPropertyProviderCustomizer.VALUE_DESCRIPTOR,
                StyleCustomizer.VALUE_DESCRIPTOR);
    }

    @Override
    protected void initProperties() throws IntrospectionException {
        super.initProperties();

        addProp("text", "Text", "The text to display in the component", CAT_DATA, PREFERRED_MASK | BOUND_MASK);
    }

    @Override
    protected void initDesc() {
        VisionBeanDescriptor bean = getBeanDescriptor();
        bean.setName("Date Selection");
        bean.setDisplayName("Date Selection Component");
        bean.setShortDescription("A component that lets allows easy start/end date selection around current date.");
    }

}

Think this may have come straight from the archetype - I don’t recall changing anything here.

All I really want to expose are 3 things - what is the current selection from my dropdown (“All”, “Today”, etc), and what the the current date the PMIDateTimePopupSelector’s are set to (startDate component and endDate component).

I don’t know if I might need two separate public properties that would keep track of the dates selected by the components, or if there’s a way to just let certain parts of those components come through through to my components beans info.

I am not really knowledgeable about beans so this part really confuses me.

Is your beaninfo actually being used?

Here’s what the archetype DesignerHook file looks like

package org.example.designer;

import org.example.client.DateSelectorComponent;
import com.inductiveautomation.ignition.common.licensing.LicenseState;
import com.inductiveautomation.ignition.designer.model.AbstractDesignerModuleHook;
import com.inductiveautomation.ignition.designer.model.DesignerContext;
import com.inductiveautomation.vision.api.designer.VisionDesignerInterface;
import com.inductiveautomation.vision.api.designer.palette.JavaBeanPaletteItem;
import com.inductiveautomation.vision.api.designer.palette.Palette;
import com.inductiveautomation.vision.api.designer.palette.PaletteItemGroup;

public class DesignerHook extends AbstractDesignerModuleHook {

    @Override
    public void startup(DesignerContext context, LicenseState activationState) throws Exception {
        // Add the BeanInfo package to the search path
        context.addBeanInfoSearchPath("com.iccworkshop.designer.beaninfos");

        // Add my component to its own palette
        VisionDesignerInterface vdi = (VisionDesignerInterface) context
                .getModule(VisionDesignerInterface.VISION_MODULE_ID);

        if (vdi != null) {
            Palette palette = vdi.getPalette();

            PaletteItemGroup group = palette.addGroup("Example");
            group.addPaletteItem(new JavaBeanPaletteItem(DateSelectorComponent.class));
        }
    }

}

So I guess not based on your highlighted line

Womp womp. Yeah, I would recommend fixing the package path. That should dramatically change how the property editor looks.

Hmm, I am still having an issue

I noticed your designer hook file also had a public static final String MODULE_ID = "component-example" that mine was missing.

for reference in case it’s something here, here is the bean info file

package org.example.designer.beaninfos;

import java.beans.IntrospectionException;

import org.example.client.DateSelectorComponent;
import com.inductiveautomation.factorypmi.designer.property.customizers.DynamicPropertyProviderCustomizer;
import com.inductiveautomation.factorypmi.designer.property.customizers.StyleCustomizer;
import com.inductiveautomation.vision.api.designer.beans.CommonBeanInfo;
import com.inductiveautomation.vision.api.designer.beans.VisionBeanDescriptor;

public class DateSelectorComponentBeanInfo extends CommonBeanInfo {

    public DateSelectorComponentBeanInfo() {
        super(DateSelectorComponent.class,
                DynamicPropertyProviderCustomizer.VALUE_DESCRIPTOR,
                StyleCustomizer.VALUE_DESCRIPTOR);
    }

    @Override
    protected void initProperties() throws IntrospectionException {
        super.initProperties();

        addProp("text", "Text", "The text to display in the component", CAT_DATA, PREFERRED_MASK | BOUND_MASK);
    }

    @Override
    protected void initDesc() {
        VisionBeanDescriptor bean = getBeanDescriptor();
        bean.setName("Date Selection");
        bean.setDisplayName("Date Selection Component");
        bean.setShortDescription("A component that lets allows easy start/end date selection around current date.");
    }

}

But that has not fixed it. I suspect that “DateSelector” is wrong but I don’t know what the right way to reference it is. I see stuff like DateSelector-client DateSelector-desiger in the pom files so I thought that was the right way to call it but clearly not. Though I guess it could be something else going on I don’t know. I also tried DateSelectorComponent as the MODULE_ID given that the designer hook bean info file has
public class DateSelectorComponentBeanInfo extends CommonBeanInfo { but that was not correct either.

That shouldn’t matter, it’s just a useful constant to have, not a requirement for anything.

So you’re currently, in designerhook.startup, adding org.example.designer.beaninfos to the ‘bean info search path’?

Any chance you can put this code up in a public way, e.g. a Github repo?