Skip to content

FEAT: Moment Plugin Framework & Search Suggestions#192

Open
kennethsn wants to merge 5 commits into
feat/169_mui-v5-upgradefrom
feat/169_animation-disable
Open

FEAT: Moment Plugin Framework & Search Suggestions#192
kennethsn wants to merge 5 commits into
feat/169_mui-v5-upgradefrom
feat/169_animation-disable

Conversation

@kennethsn
Copy link
Copy Markdown
Owner

No description provided.

@kennethsn kennethsn self-assigned this Mar 20, 2026
@kennethsn kennethsn changed the title Feat/169 animation disable FEAT: Moment Plugin Framework & Search Suggestions Mar 20, 2026
@kennethsn kennethsn requested a review from Copilot March 20, 2026 22:50
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a moment plugin framework (including a new public ./plugins entrypoint) and adds first-class search suggestions + facet serialization support for CardsBrowser-driven collection search, including an “empty landing” start mode.

Changes:

  • Add moment plugin support via MomentPlugin types, configurable momentPlugins on RootStore, and a dedicated plugins export entry (src/plugins.ts) with Rollup packaging updates.
  • Implement search suggestions (input + landing), committed-vs-draft query handling, URL facet serialization/deserialization, and empty-landing behavior for API collections.
  • Update UI/stores/build tooling: CardsBrowser suggestion UI, new localization strings, MobX action wrapping, deck.gl package split, and Rollup shims for browser bundling.

Reviewed changes

Copilot reviewed 52 out of 54 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/utils/timelineUtils.ts Adds reusable timeline direction + stable-ish key helpers.
src/utils/searchSuggestionUtils.ts Adds shared key helper for rendering suggestion lists.
src/utils/searchFacetUtils.ts Adds selected-facet deserialization for router/query-string integration.
src/utils/searchFacetUtils.test.ts Tests round-trip selected facet serialization/deserialization.
src/utils/momentConfigUtils.ts Builds moment config map with plugin overrides.
src/types.ts Introduces Moment plugin types/config map and search suggestion/facet query types.
src/state/storyStore.ts Wraps state mutations in runInAction; adds updateOptions.
src/state/storiesStore.ts Uses StoryStore.updateOptions instead of direct mutation.
src/state/searchStore.ts Adds suggestions, committedQuery, empty landing mode, and improved concurrency handling.
src/state/searchStore.test.ts Expands concurrency tests; adds suggestion behavior tests.
src/state/rootStore.ts Accepts momentPlugins; builds config map via utility; renames StoryId action plugin type.
src/state/moments/iframeMomentStore.ts Makes message access tolerant of missing iframe data.
src/state/momentStore.ts Wraps a few mutations (reset, reloadComponent, setMuiTheme) in runInAction.
src/state/localeStore.ts Wraps locale mutation in runInAction.
src/state/geoMapStore.ts Switches to @deck.gl/core imports; wraps several mutations in runInAction.
src/state/collectionsStore.ts Removes async runInAction; updates collection delete to be action-wrapped.
src/state/collectionStore.ts Adds emptyLanding/search default behavior + suggestion config; refactors init/search flows.
src/state/collectionStore.test.ts Tests emptyLanding initial-load deferral and default-query behavior.
src/state/avStore.ts Wraps isPlaying mutation in runInAction.
src/plugins.ts New public plugins entrypoint exports moment components/stores for consumers.
src/hooks/useElementIsVisible.ts Fixes observer lifecycle/cleanup and adds effect deps.
src/hooks/useCardsBrowserSearch.ts New hook managing keyboard nav + animated transitions for suggestions.
src/hooks/index.ts Exports useCardsBrowserSearch.
src/configs/momentConfig.ts Moves MomentConfig types to types.ts; uses shared config map type.
src/configs/localizationConfig.ts Adds new translation keys for search placeholders/suggestion headings.
src/components/UI/Timeline/Timeline.tsx Uses extracted timeline utils and improved event keys.
src/components/UI/StoryId/StoryId.types.ts Renames StoryId action type to StoryIdActionPlugin.
src/components/UI/StoryId/StoryId.helpers.ts Renames StoryId action type to StoryIdActionPlugin.
src/components/UI/MenuTooltip/MenuTooltip.tsx Prevents popover open state when there’s no anchor element.
src/components/UI/GeoMap/GeoMap.types.ts Switches PickingInfo import to @deck.gl/core.
src/components/UI/CardsBrowser/CardsBrowserSearch.tsx Adds input/landing suggestion UI + inline loader; wires new hook.
src/components/UI/CardsBrowser/CardsBrowserLandingSuggestions.tsx New landing suggestions component (chips).
src/components/UI/CardsBrowser/CardsBrowserInputSuggestions.tsx New input suggestions overlay (keyboard + hover).
src/components/UI/CardsBrowser/CardsBrowser.types.ts Adds suggestion-related prop types; adds isLoading prop to search.
src/components/UI/CardsBrowser/CardsBrowser.tsx Passes loading state into search; adjusts loader behavior.
src/components/UI/CardsBrowser/CardsBrowser.styles.ts Adds styles/animations for suggestion overlays and landing chips.
src/components/StoryMoment/StoryMoment.tsx Supports plugin-provided moment components (string lazy import vs direct component).
src/components/StoriesAPICollection/StoriesAPICollection.tsx Reads/writes facets query param; passes default facets/query into CollectionStore.
src/components/Moments/IFrameMoment/index.ts Adds index barrel export for plugin consumption.
src/components/Moments/CardsBaseMoment/index.ts Adds index barrel export for plugin consumption.
src/components/Moments/BaseMoment/index.ts Adds index barrel export for plugin consumption.
src/components/Moments/AVBaseMoment/index.ts Adds index barrel export for plugin consumption.
src/components/Collections/Collections.stories.tsx Refactors story to build CollectionStores with a real RootStore.
src/components/CollectionStoriesList/CollectionStoriesList.tsx Hides stories list when collection indicates it should be hidden.
src/components/CollectionSection/CollectionSection.tsx Applies new badge style override via sx.
src/components/CollectionSection/CollectionSection.styles.ts Adds badge styling to inherit font size.
src/components/CollectionSection/CollectionSection.stories.tsx Adds Storybook coverage for CollectionSection with a real store.
src/components/CollectionLayout/CollectionLayout.tsx Avoids rendering single-story card when story is missing; uses new header gating flag.
src/components/Collection/Collection.stories.tsx Adds single-story “tool layout” story to validate list hiding.
rollup.config.mjs Adds loaders.gl shims, better externals, dynamic import vars support, and plugins build output + dts.
package.json Updates deps (split deck.gl packages, bump maplibre, add ./plugins export, version bump).
configs/loadersGlNodeShimPlugin.mjs New Rollup plugin to shim node built-ins leaking from loaders.gl worker-utils.
.gitignore Ignores .npmrc.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/timelineUtils.ts
Comment on lines +158 to +162
'@keyframes cardsBrowserSuggestionFadeIn': {
from: {
opacity: 0,
transform: 'translateY(6px)',
},
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The @keyframes definitions are embedded inside searchSuggestionOption(...), which is called for every rendered suggestion option. This can lead to duplicate keyframe injection and unnecessary style churn. Consider moving these keyframes to a static/top-level style definition (or a shared @global/keyframes() helper) and referencing them from each option.

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +57
<SearchInput
fullWidth
onBlur={handleSearchInputBlur}
onFocus={handleSearchInputFocus}
onKeyDown={handleSearchInputKeyDown}
placeholder={search.placeholder}
search={search}
sx={styles.searchInput}
variant="standard"
/>
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

SearchInput defines its own Enter/Shift+Enter handling (Shift+Enter bypasses cache). Passing onKeyDown={handleSearchInputKeyDown} here overrides that internal handler (because SearchInput spreads ...textFieldProps after its own props), so Shift+Enter bypass-cache behavior is lost. Consider either updating handleSearchInputKeyDown to preserve the existing behavior (e.g., when no suggestion is selected, call search.submit(false, event.shiftKey)), or adjusting SearchInput to compose/chain its internal onKeyDown with a provided handler instead of being overridden.

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +92
const applySuggestionWithTransition = async (
suggestion: SearchSuggestion,
location: SuggestionLocation,
) => {
if (isTransitioningSuggestion) {
return;
}

const key = getSuggestionKey(location, suggestion);
setTransitioningSuggestionLocation(location);
setTransitioningSuggestionKey(key);

if (location === 'input') {
// Close the input menu immediately once a selection is made.
search.setFocused(false);
setActiveSuggestionIndex(-1);
}

if (location === 'landing') {
setTransitionLandingSuggestions([...search.landingSuggestions]);
}

setTransitionPhase('itemFade');
await delay(ITEM_FADE_DELAY_MS);

// Phase 2: search fires, loader appears while section stays visible.
setTransitionPhase('searchWait');

try {
await search.applySuggestion(suggestion);

// Phase 3: search is done; section fades out before results render.
setTransitionPhase('sectionFade');
await delay(SECTION_FADE_DELAY_MS);
} finally {
setTransitionPhase(null);
setTransitioningSuggestionLocation(null);
setTransitioningSuggestionKey(null);
setTransitionLandingSuggestions([]);
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

applySuggestionWithTransition awaits delay(...) and then calls multiple setState calls. If the component using this hook unmounts during the transition/search, these pending timeouts/promises can still resolve and trigger state updates on an unmounted component. Consider adding a cancellation/"isMounted" guard (or clearing timeouts via refs) so the transition aborts cleanly on unmount.

Copilot uses AI. Check for mistakes.
Comment on lines 439 to +444
reset() {
this.collection = deepCopy(this.initialCollection);
this.init();
this.isEdited = false;
runInAction(() => {
this.collection = deepCopy(this.initialCollection);
this.init();
this.isEdited = false;
});
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

reset() calls the async this.init() inside runInAction. Invoking async work inside runInAction is fragile (the action is meant to be synchronous) and can make MobX action boundaries harder to reason about. Consider limiting the action to synchronous state updates (collection/isEdited) and then calling this.init() outside the runInAction block (and optionally handling the returned promise).

Copilot uses AI. Check for mistakes.
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