Skip to content

FIREFLY-2033: React Component Library for Firefly Visualizations#1970

Open
loitly wants to merge 1 commit into
devfrom
FIREFLY-2033-component-library
Open

FIREFLY-2033: React Component Library for Firefly Visualizations#1970
loitly wants to merge 1 commit into
devfrom
FIREFLY-2033-component-library

Conversation

@loitly

@loitly loitly commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Ticket: https://jira.ipac.caltech.edu/browse/FIREFLY-2033

his PR introduces a React component library that wraps Firefly’s table, chart, and image visualization components and packages them as a standalone npm library.

The library source is located under /component-library.

Documentation

  • The top-level README.md is intended for library consumers and provides installation and usage instructions.
  • A developer-focused developer-guide.md (not published with the package) is included in the component library directory and contains implementation details, development workflows, and development guidlines.

Development and Build Modes

The component library has three modes of operation:

1. Distributed Mode

Components are built and packaged for publication to npm.

  • Build artifacts are generated under ./dist.
  • Intended for package distribution and downstream consumption.

2. Storybook Development Mode

Interactive development environment for component development and testing.

  • Enables rapid iteration on existing components.
  • Provides a framework for developing and validating new components.
  • Includes live examples and documentation.

3. Storybook Static Mode

Generates a fully static Storybook build suitable for deployment.

  • Produces a complete documentation and demonstration site.
  • Intended for publishing and sharing component documentation.

Firefly Integration

As part of the (full) Firefly build process, a deployed Storybook instance for this library is published at:

/firefly/component-library/

This deployment serves as the primary reference for component examples, documentation, and interactive testing.

Notes for Reviewers

There are additional implementation and architectural details that may be easier to discuss in person.

For this review, please focus on:

  • Exploring the Storybook examples.
  • Reviewing the component documentation.
  • Evaluating the overall developer experience.
  • Providing feedback on API design, documentation clarity, and component organization.

Demo: https://fireflydev.ipac.caltech.edu/firefly-2033-component-library/firefly/component-library/

NOTES:
The Java files in this branch are from a separate fix (multiple webapp local deployment) that I accidentally included. I can pull them out if you'd prefer to keep this PR focused.

@loitly loitly added this to the 2026.2 milestone Jun 11, 2026
@loitly loitly self-assigned this Jun 11, 2026

@robyww robyww left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Overall it looks really good. There are so many files here it is hard to take it all in. These are my initial comments.

Things I really like

  • The storybook demo and documentation is pretty cool. This will really help.
  • The components in general and how easily usable they are.
  • The npm packages though I doin't have my head around it all yet.
  • A clear way to add more components.

Things I don't yet understand

  • the role of vite?
  • Rollup bundling? are we using rollup?
  • should be replace webpack with vite in general?
  • Are out input component working with html forms?. I think they are going to have to.

Comments

  • How about the default chart?
  • image, table, and chart components need to take arrays for url or request
  • It is often more readable to import {string,bool,object,etc} from prop-types instead of using PropTypes.bool etc.


import Enum from 'enum';

// ─── From ImagePlotCntlr.js ──────────────────────────────────────────────────

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad you did this. I have been meaning to for a lone time.

</Tab>
<Tab key='ImageSearchBlue' name='blue' label={<div style={{width:40, color:'blue'}}>Blue</div>}>
<SingleChannel {...{key: FG_KEYS.blue, groupKey: FG_KEYS.blue, imageMasterData, multiSelect, archiveName, noScroll}}/>
<SingleChannel key={FG_KEYS.blue} {...{groupKey: FG_KEYS.blue, imageMasterData, multiSelect, archiveName, noScroll}}/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you have to break this up like this? was it not picking up the key?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In React 19, key is now treated as a regular prop. However, because object spread operations pass the key as generic data rather than an explicit structural identifier, it bypasses React's Virtual DOM tracking engine, causing React 19 to trigger a runtime warning.

<Stack {...{width:1, height:1, flexWrap:'nowrap', alignItems:'stretch'}}>
<Stack {...{flex: '1 1 auto', direction:'row'}}>
<ImageExpandedMode {...{key:'results-plots-expanded', closeFunc, viewerId}}/>
<ImageExpandedMode key='results-plots-expanded' {...{closeFunc, viewerId}}/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see as below, why break it up?

Comment on lines +17 to +18
setRootURL(serverUrl);
return firefly.bootstrap({}, { serverUrl, ...appOptions });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first line should probably be

    if (window?.firefly?.initialized) return Promise.resolve(); 

firefly.bootstrap does do this but I don't think we want to call setRootUrl twice.

Comment thread component-library/src/images/index.js Outdated

// ─── Utils ───────────────────────────────────────────────────────────────────

export * from 'firefly/visualize/VisUtil.js';

@robyww robyww Jun 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not totally sure if this is necessary. However, I am sure PlotViewUtil.js should be exported. Actually there is a lot, we need to think about it. An attempt at this is ApiUtilImage.jsx

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we can selectively export only those we want to publish.

* - **Full-screen expand**: expand the image to fill the viewport.
* - **Advanced requests**: supply a `WebPlotRequest` for service-based or workspace images.
*/
export function ImagePlot({ plotId: plotId_prop, viewerId: viewerId_prop, url, request, sx }) {

@robyww robyww Jun 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably need to all url and request properties to optionally be an array.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. We'll handle this in a separate ticket.

Comment on lines +84 to +91
__PROPS__: {
BUILD_ENV: JSON.stringify('dev'),
SCRIPT_NAME: JSON.stringify([]),
MODULE_NAME: JSON.stringify('firefly-component-library'),
MIN_SAFARI_VERSION: '17',
MIN_CHROME_VERSION: '130',
MIN_FIREFOX_VERSION: '134',
MIN_EDGE_VERSION: '130',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is now in two places

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was hard-coded in webpack. I'll try to move them into configurable props.

<Card {...{
color:'warning', variant:'soft', position:'relative',
zIndex: maskShowing ? 10 : 'auto',
color:'warning', variant:'soft',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how did you decide the zIndex was not necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since position and zIndex are not defined properties of Card, and the statusTextSx property in the next line (line 90) defines and overrides them, they should be removed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I second this, I noticed it when using BGMaskPanel for image type job results.


**Storybook performance.** `@storybook/react-vite` serves stories through Vite's native ES module dev server. Each story loads only its own imports on demand, so the dev server starts immediately and individual stories load in milliseconds. A webpack-based Storybook must build an upfront bundle before any story can render, which in this codebase produces a 9+ MB bundle.

**Inline Web Worker.** Firefly loads one of its workers using webpack's `worker-loader` import convention. Vite handles the equivalent case natively via the `?worker&inline` query suffix. A small transform plugin in `vite.config.js` rewrites the webpack-style import to the Vite form, keeping the Firefly source unchanged while making the worker work correctly in the library build.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

webpack is advising a change in how this is done. I was going to look into it. I wonder if it will break something.

@robyww

robyww commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

More Comments:

  • the test does not appear to be built debug. Have you tried loading a source map
  • task.gincl sets up all sort of properties that get put into the webpack build. are these included?
  • Our api create a window.firefly we should do something like this as well
    • for example- maybe the most important window.firefly.getVersion()

React component library wrapping Firefly's table, chart, and image
visualization components for use as a standalone npm package.
Includes Storybook for interactive development and documentation.
@loitly loitly force-pushed the FIREFLY-2033-component-library branch from 8923eeb to 02f3ee6 Compare June 17, 2026 21:09
@loitly

loitly commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author
  • the test does not appear to be built debug. Have you tried loading a source map

Use yarn storybook for dev-mode hot redeploy with source map.

  • task.gincl sets up all sort of properties that get put into the webpack b ild. are these included?

I'll look to add this into this PR.

  • Our api create a window.firefly we should do something like this as well

    • for example- maybe the most important window.firefly.getVersion()

This is uncommon for a library but we can add it if you like.

@jaladh-singhal jaladh-singhal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@loitly this is excellent work! The component library looks very neat and storybook demo renders code documentation very nicely along with rendering the examples 👏

I tried to go over most of the files except the vite.config.js which seems a lot of configuration tweaking to make our existing codebase designed for webpack play nice with vite/rollup?! The code looks mostly good and clean. Most of my comments are minor improvements or broad questions.

I haven't build it locally yet. The storybook demo online looks awesome. I did notice one issue with the chart stories however:
In "Charts Panel", click on "Static Data" then -> "Linked table" -> "Event Hooks": all components load fine. Now click on "Linked table" then -> "Event Hooks": the chart panel component disappears. Same behavior in "Histogram Chart" and "Scatter Chart" stories when you click on them top to bottom and then go bottom to top. Maybe it has to do with some chart viewer ID collision? I don't see any traceback so can't tell.


The following are some big ideas/directions we should consider after we merge this PR:

  1. Make the components skinnable (easy-to-change styling/look): HEASARC has a style guide (design system) they have to follow and IIRC NED also wanted to do a different look than default firefly styling. I know we are limited by the styling machinery of JoyUI but:
    • we can maybe create some stories to demo customizing components look and point to relevant documentation in joy-ui docs explaining how to customize the styling
    • [super ambitious high-effort route] redo our firefly components to use https://v5.mui.com/base-ui/ and then use joy ui as the default styling layer rather than as the component layer.
  2. We should consider using Vite/Rollup also for the firefly/src instead of webpack. The Vite seems to be industry standard now-a-days and requires much lesser config setup than we do with webpack.
  3. We should also be deliberate about putting param/props description in jsdoc docstring and minimize PropTypes since it's discouraged in react 19+. And maybe incrementally add typescript?!

<Card {...{
color:'warning', variant:'soft', position:'relative',
zIndex: maskShowing ? 10 : 'auto',
color:'warning', variant:'soft',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I second this, I noticed it when using BGMaskPanel for image type job results.


A React component library for astronomical data visualization, built on
[Firefly](https://github.com/Caltech-IPAC/firefly), the web visualization
framework from [IPAC / Caltech](https://www.ipac.caltech.edu/).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
framework from [IPAC / Caltech](https://www.ipac.caltech.edu/).
framework from [Caltech/IPAC](https://www.ipac.caltech.edu/).

that's what the recommended way is for written materials.

framework from [IPAC / Caltech](https://www.ipac.caltech.edu/).

The library provides ready-to-use components for displaying catalog tables,
interactive charts, and FITS sky images that integrate with a running Firefly

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mention: FITS and HiPS sky images? or astronomical images?

@@ -0,0 +1,67 @@
{
"name": "@ipac/firefly-component-library",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just do @ipac/firefly-components for brevity. NPM package implicitly implies that it's a library and we also explain that in description.

"@mui/joy": "^5.0.0-beta.52",
"@mui/material": "^5.18",
"@mui/x-date-pickers": "^6.19",
"moment": "~2.30",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is ~ on moment deliberate? In general, peerDependencies should be broad version ranges (hence ^)

@@ -0,0 +1,6 @@
import { Meta, Markdown } from '@storybook/blocks';
import Readme from '../README.md?raw';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is great!

(Story, ctx) => {
const desc = ctx.parameters.storyDescription;
return (
<CssVarsProvider defaultMode='system' theme={theme}>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way we can expose and pass the mode from storybook layer to firefly components?

Currently I can't test the components in storybook stories in dark mode even when my system theme is dark.

* For large row counts Firefly automatically switches from SVG `scatter`
* to WebGL `scattergl` for performance.
*/
export function ScatterChart({ chartId, tbl_id, x_axis, y_axis, chartData, options, events, ...props }) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to make Scatter and Histogram separate components? They seem like a specific case of ChartPanel controlled via chartData prop - can't we just document that in ChartPanel's story as more cases?

In broader context: are we ok with this component library API not mirroring the existing API of main firefly source? I can see some advantages for not mirroring but it may create a shadow API that's hard to maintain if overused.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of my work involves demonstrating our capabilities. This example show how to simplify our existing components. By keeping the Scatter chart as it is, we can hide the internal mapping scheme used to map a table column’s data to a chart’s data array. Also, it highlights the required parameters.

However, I understand your concern. Another idea is to move these components into Firefly’s source code so that Firefly can utilize them instead of maintaining them as a separate component within the library.

These are considerations to keep in mind before the actual release.

import { DEFAULT_COVERAGE_VIEWER_ID } from 'firefly/visualize/PlotViewUtil.js';

/**
* Displays table coverage/footprint overlaid on a sky image.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Displays table coverage/footprint overlaid on a sky image.
* Displays table coverage/footprint overlaid on a sky image as markers.

*/
request: PropTypes.object,

/** MUI Joy `sx` prop applied to the container. Use to set height, width, etc. */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these param descriptions render nicely in storybook demo. But I'm wondering if we can move them to jsdoc above since PropTypes are discouraged in React 19+? (which will also remove proptype as ome more dependency)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vaguely recalled encountering errors while storybook parses the jsdoc, but it’s worth revisiting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants