Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
281 changes: 51 additions & 230 deletions apps/web/src/stories/export-preview-story-harness.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const meta = {
docs: {
description: {
component: `
Shared \`FormatOptionsPanel\` is the component boundary around the existing export/import option-panel classes.
Shared \`FormatOptionsPanel\` renders declarative export/import option definitions through the component contract.

This Basic section maps to the non-code tabs in the current product:
- \`CSV\` and \`DSV\` for delimiter, header, and quoting controls
Expand Down
85 changes: 84 additions & 1 deletion apps/web/src/stories/import-export-workspace.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,25 @@ function renderImportExportWorkspaceStory(args) {
const storyGrid = new StoryMemoryGrid(createSampleGridData());
const exporter = new Exporter(storyGrid);
const importer = new Importer(storyGrid);
if (args.unsupported === true) {
exporter.canExport = () => false;
importer.canImport = () => false;
}
const component = createImportExportWorkspaceComponent({
root,
documentObj: document,
props: {
previewRowLimit: args.previewRowLimit,
importBusy: args.importBusy,
exportBusy: args.exportBusy,
importStatusMessage: args.importStatusMessage,
importStatusLoading: args.importStatusLoading,
exportStatusMessage: args.exportStatusMessage,
exportStatusLoading: args.exportStatusLoading,
errorStatusMessage: args.errorStatusMessage,
},
services: {
requestConfirm: async () => true,
},
});

Expand Down Expand Up @@ -83,13 +97,21 @@ const meta = {
docs: {
description: {
component:
'ImportExportWorkspace is the Phase 6 app-side feature boundary for import/export controls, format selection, text preview/editing, and options-panel composition.',
'ImportExportWorkspace is the app-side component-owned import/export feature. It composes the toolbar, format selector, options panel, preview/edit text editor, row-count control, file import, copy, and download behavior without delegating to the old legacy controls.',
},
},
},
args: {
format: 'csv',
previewRowLimit: 10,
importBusy: false,
exportBusy: false,
importStatusMessage: '',
importStatusLoading: false,
exportStatusMessage: '',
exportStatusLoading: false,
errorStatusMessage: '',
unsupported: false,
},
argTypes: {
format: {
Expand Down Expand Up @@ -151,3 +173,64 @@ export const JsonPreview = {
await expect(applyButton).toBeEnabled();
},
};

export const ImportBusy = {
args: {
importBusy: true,
importStatusMessage: 'Importing full data into grid...',
importStatusLoading: true,
},
parameters: {
docs: {
description: {
story:
'Shows the workspace while an import is active. The Set Text From Grid and Download affordances become busy/disabled, and the import status remains visible with loading styling.',
},
},
},
};

export const ExportBusy = {
args: {
exportBusy: true,
exportStatusMessage: 'Generating export text...',
exportStatusLoading: true,
},
parameters: {
docs: {
description: {
story:
'Shows the workspace while a download/export is active. The download button is disabled and the export status explains the current operation.',
},
},
},
};

export const UnsupportedFormat = {
args: {
format: 'json',
unsupported: true,
},
parameters: {
docs: {
description: {
story:
'Shows how the toolbar renders when the selected format is not importable or exportable. Import controls and download are hidden while the selector/editor remain mounted.',
},
},
},
};

export const ErrorState = {
args: {
errorStatusMessage: 'Import failed. Check file format/options.',
},
parameters: {
docs: {
description: {
story:
'Shows the persistent error/status surface used when import/export validation or parsing fails. Retry Set Grid From Text after editing the preview text.',
},
},
},
};
2 changes: 1 addition & 1 deletion apps/web/src/stories/text-preview-editor.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const meta = {
docs: {
description: {
component:
'TextPreviewEditor is the Phase 6 app-side component that owns preview/edit controls, the shared text area, and the options/preview split-panel shell.',
'TextPreviewEditor is the app-side preview/edit component. It renders the Preview/Edit toggle, Auto Preview checkbox, compact preview row-count spinbutton, shared text area, copy button, and options/preview split-panel shell.',
},
},
},
Expand Down
12 changes: 10 additions & 2 deletions apps/web/src/tests/jest/utils/help-content-coverage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,16 @@ describe('help content coverage', () => {
});

test('options panel help icons resolve to inline text or help entries', () => {
const optionsDir = path.join(repoRoot, 'packages', 'core-ui', 'js', 'gui_components', 'options_panels');
const panelFiles = fs.readdirSync(optionsDir).filter((filename) => filename.endsWith('.js'));
const optionsDir = path.join(
repoRoot,
'packages',
'core-ui',
'js',
'gui_components',
'shared',
'format-options-panel'
);
const panelFiles = ['format-option-panel-definition.js', 'format-option-help.js'];

const missing = [];
panelFiles.forEach((filename) => {
Expand Down
8 changes: 7 additions & 1 deletion docs/frontend-component-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,13 @@ Storybook is a review, documentation, and lightweight interaction-example layer.
- When practical, presenter stories should include a destroy-and-remount example so reviewers can confirm lifecycle safety without reading Jest tests first.
- Storybook cleanup is centralized in `.storybook/preview.js`; stories may expose `root.__storybookCleanup`, and the global decorator will run that teardown before the next story and remove common body-level artifacts such as modals, method-picker overlays, tooltip poppers, and inline help containers.
- Prefer returning the story root directly instead of manually appending it to `document.body` unless the component behavior genuinely depends on top-level overlays or body-scoped positioning.
- Current intentional body-aware Storybook exceptions are the app page bootstrap story and the export-preview harness, because both still exercise document-scoped page/bootstrap behavior rather than a purely root-scoped component contract.
- Current intentional body-aware Storybook exception is the app page bootstrap story, because it still exercises document-scoped page/bootstrap behavior rather than a purely root-scoped component contract.

## Format Options

`FormatOptionsPanel` renders format-specific option panels from declarative definitions under `shared/format-options-panel/`. A format option panel definition owns the field schema and returns a panel object with `render`, `read`, `write`, `validate`, `setDirty`, `destroy`, and `onApply` behavior. The shared view mounts those panels directly; there is no separate legacy `options_panels/*` class layer or `addToGui()` adapter path.

Option sanitization remains in the generator/app options services, not in the view. The view reads user input into an explicit `{ outputFormat, options }` payload and the controller sends that payload through the injected sanitization service before emitting applied options.

## Test Layering

Expand Down
8 changes: 7 additions & 1 deletion docs/frontend-component-migration-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ This is an internal engineering plan for moving the current plain JavaScript UI

This plan intentionally does not require React, Vue, Svelte, or Lit. The structure should make a future Lit migration possible by replacing `ThingView` with a Lit element while keeping controllers, services, and adapters stable.

## Status Note

The first component migration established the main page and feature component boundaries, but later review found that several componentized shells still delegate core behavior to older controls, DOM helpers, or compatibility facades. For the stricter goal of eliminating legacy UI approaches before a future React, Web Components, or Lit migration, use `docs/frontend-legacy-ui-elimination-plan.md` as the active follow-on plan. That document supersedes any "complete" status in this migration checklist where old UI orchestration still remains underneath a component boundary.
Comment thread
eviltester marked this conversation as resolved.

For the related test-layer cleanup needed after app/generator component sharing increased, use `docs/frontend-ui-matrix-rationalization-plan.md` to track reduction of redundant app-vs-generator UI matrix coverage.

## Goals

- Split the app and generator UIs into feature components that can be mounted independently.
Expand Down Expand Up @@ -363,7 +369,7 @@ Current status:
- `ImportExportWorkspace` now lives under `app/import-export-workspace/` with a controller, view, and create-component factory.
- `ImportExportToolbar`, `FormatSelector`, and `TextPreviewEditor` now live under their own app feature folders and are mounted through `ImportExportWorkspace` rather than through direct page bootstrap wiring.
- The app bootstrap now mounts the import/export area through `createImportExportWorkspaceComponent(...)`, and the `tabbedTextArea` preview region now lives inside that feature boundary instead of as a separate sibling shell.
- The extracted workspace reuses the shared `FormatOptionsPanel` while preserving the current import/export behavior through the existing import/export controls path underneath.
- The extracted workspace reuses the shared `FormatOptionsPanel`, and the current import/export behavior now runs through the workspace controller/services path instead of the old import/export controls path underneath.
- File input, drag/drop, file reading, download, and clipboard behavior now flow through explicit Phase 6 app-side adapters/services under `app/import-export-adapters/`, so the legacy import/export control layer no longer talks directly to `FileReader`, `DragDropControl`, or download/copy browser APIs.
- Storybook now documents `ImportExportWorkspace`, `ImportExportToolbar`, `FormatSelector`, and `TextPreviewEditor` directly with small component stories instead of relying only on the older broader app/export harnesses.
- The export-format preview stories now mount through `ImportExportWorkspace` via a dedicated `export-preview-story-harness.js` helper rather than the older split `ImportExportControls + TabbedTextControl` story path or the broader omnibus `storybook-harnesses.js` module.
Expand Down
Loading