Skip to content

Make FilmDrop UI into a component - #579

Open
Xenocide122 wants to merge 49 commits into
mainfrom
rng/filmdrop-ui-component
Open

Make FilmDrop UI into a component#579
Xenocide122 wants to merge 49 commits into
mainfrom
rng/filmdrop-ui-component

Conversation

@Xenocide122

@Xenocide122 Xenocide122 commented May 5, 2026

Copy link
Copy Markdown
Contributor

Summary

This branch reframes FilmDrop UI from a single standalone application into a first-class, npm-distributable React component library that still supports a local SPA dev/demo entry. Practically, that means a published dist bundle, explicit package.json exports (JS + types + CSS), peer dependencies for the host app’s React/MUI/Redux/Router/Leaflet stack, a library entry separate from the SPA entry, and consumer-facing docs, starter app, and verification scripts so teams can embed FilmDrop inside their own apps, not only run the repo as its own site.

Alongside that packaging shift, the branch includes substantial hardening work (services, map module split, Redux/router correctness, tests, CI) that supports reliable embedding in foreign hosts and stricter library boundaries.

Changes (high level)

  • Primary: App → component/library model: Ship filmdrop-ui as an installable package with main/module/types, exports map, and files limited to dist + key docs, not “clone and run” as the only consumption story.
  • Dual entrypoints: SPA bootstrap (index.jsx + Leaflet CSS at the app boundary) vs library bundle (lib-entry / Vite lib build) so Leaflet/CSS ownership and bundling match how consumers integrate.
  • Host-owned dependencies: Move integration surface to peerDependencies (React, MUI, Redux Toolkit, TanStack Router, Leaflet/React-Leaflet, etc.) so the library fits normal app supply chains.
  • Embeddable root: Introduce FilmDropRoot and restructure App so the UI is composed as a mountable subtree suitable for embedding, not only a monolithic app shell.
  • TypeScript consumer surface: Add generated/published .d.ts so TypeScript apps can import the package cleanly.
  • Starter + smoke verification: Add examples/starter and scripts to validate types, library bundle, and consumer smoke flows; evidence the component works outside this repo’s original app layout.
  • Build/release pipeline: Add library Vite config, release workflow, and CI changes aligned with publishing rather than only internal dev.
  • Supporting refactors for “library correctness”: Normalize STAC request headers/errors across services, stabilize router/URL state, split map utilities for maintainability, improve Redux boundaries/selectors, expand tests and test harnesses (renderFilmDrop, header factories, etc.).
  • Docs/legal packaging: Expand contributor/testing docs, changelog, notice, and README positioning to describe library + optional SPA usage.
  • Added config, urlState, and onUrlStateChange support to FilmDropRoot.
  • Routed URL reads and writes through a shared controller for both router-owned and controlled modes.

Xenocide122 and others added 16 commits April 20, 2026 09:28
…ernals verification, & release-workflow hardening

Co-authored-by: Copilot <copilot@github.com>
…seUrlStateSync deps fix, starter example, +14 tests

Co-authored-by: Copilot <copilot@github.com>
…rter/types verify scripts; fix EnhancedDetails CSS ownership so it imports its own stylesheet instead of relying on PopupResults.

Co-authored-by: Copilot <copilot@github.com>
…types, starter workspace tests), promote audit-prod to blocking, and add least-privilege permissions and concurrency cancellation.
Co-authored-by: Copilot <copilot@github.com>
Tightens hook and context patterns to remove render thrash and stale
state risks:

- TextField: replace prop-sync useEffect with render-phase setState.
- NumericRangeInputs: consolidate twin effects into one.
- PopupResults: consolidate dispatching effects; track stable deps.
- EnhancedDetailsDisplay: dedupe error alerts via lastErrorKeyRef.
- LinkItem, PopupResult, OverflowTooltip: add isMountedRef guards.
- LayoutContext, EnhancedDetailsContext: memoize provider values.
- useUrlInitialize, useUrlStateSync: rename refs to *Ref convention.
- useResizablePanel: stabilize updateColumns via ref-mirrored flag.
- App.jsx, LeftContent: correct effect dep arrays.

Adds tests: TextField (3), LayoutContext memo, NumericRangeInputs
no-clobber, PopupResults dispatch-count.
Wrap each returned function in useCallback and the returned object
in useMemo so consumers can list these callbacks in useEffect deps
without causing re-runs every render.

Adds two reference-identity tests: stability across rerenders with
unchanged params, and invalidation of collectionId-dependent
callbacks when the collection changes.
Mirrors the LayoutContext memoization test: a memoized probe consumer
verifies that re-rendering the provider with an unrelated prop change
does not re-render consumers that read context, locking in the
useMemo'd provider value contract.
When children transitions from non-empty to empty, the early-return
guard previously left isOverflowing at its prior value. Reset it to
false so a stale true cannot persist; React's primitive-setState
bailout avoids any extra render when the value is already false.
The 'rapid result changes' test now asserts an exact count (2 — one
dispatch per results-identity change across two rerenders) instead of
the previous loose <= 2 bound. Adds a comment near the consolidated
effect explaining why _currentPopupResult is in deps despite being
written by the effect (the includes-guard plus Immer's identity-bail
prevent the apparent loop).
Both fields are slice-local: dateTime has no reducer/dispatch and
showVisualizationList has a reducer that is never dispatched and a
state field that is never read. No behavior change.
The action was dispatched on collection change in CollectionDropdown
but the resulting state was never read by any production code path.
The only consumer was a test asserting on the dispatch side effect.
Removing the state, action, dispatch, and the tautological test.
Dispatched once in clearSearch but never read by any component or
selector. Removed state, action, export, the import in searchHelper,
and the dispatch in clearSearch.
The mainSlice was inconsistent: most actions used `set` + PascalCase
(setSearchResults, setShowZoomNotice) but 26 used `set` + camelCase
prefix (setisDrawingEnabled, setappConfig, setmappedScenes, etc.).

Renamed all 26 to the PascalCase convention. State field names
unchanged. Pure mechanical rename across 52 files; no behavior change.

Also: in CollectionDropdown, change ` git diff src/redux/slices/mainSlice.js | head -60 && echo --- && git diff src/components/CollectionDropdown/CollectionDropdown.jsxselectedCollectionData` to
` git diff src/redux/slices/mainSlice.js | head -60 && echo --- && git diff src/components/CollectionDropdown/CollectionDropdown.jsxselectedCollectionData?.id` so the effect body's read matches the
dependency array, avoiding a subtle exhaustive-deps inconsistency.
clearSearch dispatched 16 sequential actions to reset every
search-derived state field. Each dispatch ran every subscriber
selector, causing React to schedule and batch ~16 reconciliation
passes per call.

Replace with a single `resetSearchState` compound reducer in
mainSlice that performs the same field resets atomically. The
reducer is documented with the persistent-UI fields it deliberately
leaves untouched (selectedCollection, viewMode, theme, etc.).
clearSearch now: clear map layers \u2192 dispatch resetSearchState() \u2192
navigate. Same fields cleared, same URL transitions, same observable
behavior; one dispatch instead of 16.
@element84-infra

element84-infra commented May 5, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Xenocide122 and others added 13 commits May 5, 2026 09:01
Co-authored-by: Copilot <copilot@github.com>
Preserve map overlays across remount by detaching data layers without clearing them, while still clearing ephemeral layers.

Use one-time active-router snapshots for mount-only URL seeds in LeafMap and ViewSelector to avoid reactive useSearch subscriptions.

Migrate RightContent test setup to the renderFilmDrop harness with a fresh store and keep module-scope router mocks.
Xenocide122 and others added 20 commits May 5, 2026 14:25
- Remove redundant 'Reads colors from CSS' comments in mapStyles
- Fix VisualizationDropdown import consolidation and accessibility
- Add missing useEffect dependency (setViz)
- Associate form label with checkbox control via htmlFor/id
- Delete mapHelper barrel; migrate imports to direct modules
- App.jsx and mapHelper.test.js now import from mapLayers/mapStyles

All 768 tests passing. Quality gates clean (lint, format, typecheck).
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants