diff --git a/Documentation/blueprints/application-hierarchy.md b/Documentation/blueprints/application-hierarchy.md index c58cf3e..cfb50dc 100644 --- a/Documentation/blueprints/application-hierarchy.md +++ b/Documentation/blueprints/application-hierarchy.md @@ -82,7 +82,7 @@ change slice **opens its dialog** instead of announcing the command: - The button navigates to the dialog destination `sliceDialogDestination(resolved)` - the template `sliceDialogFor` picked, with the `slice`, `command` and `title` as parameters. The host renders `dialogs[template]` when the application authors one (the `core:sliceDialog` component, or its own element, reading `currentDialogParameters`), and the built-in **`SliceDialogView`** otherwise. -- The dialog is a centred modal card: the button title, the subtitle `State Change · `, a labelled field per command property, +- The dialog is a centred modal card: the button title, a labelled field per command property, and `Cancel` with a submit button labelled with the title. The first field takes focus, Tab stays inside, Escape cancels, and focus goes back to the button. Required fields are checked before anything is invoked. - The fields come from the command, through `SliceCommandFieldsProvider` (`fieldsFor(command, slice)` returns `{ name, type, label?, required? }` diff --git a/Source/JavaScript/blueprint.default/preview-host/for_ScenePreviewHost/when_previewing_a_state_change_slice.tsx b/Source/JavaScript/blueprint.default/preview-host/for_ScenePreviewHost/when_previewing_a_state_change_slice.tsx index 6f86de5..720aa98 100644 --- a/Source/JavaScript/blueprint.default/preview-host/for_ScenePreviewHost/when_previewing_a_state_change_slice.tsx +++ b/Source/JavaScript/blueprint.default/preview-host/for_ScenePreviewHost/when_previewing_a_state_change_slice.tsx @@ -24,7 +24,7 @@ describe('when previewing a State Change slice', () => { }); it('should open the task dialog of the slice and draw nothing of the page of the feature behind it', () => [dialog()!.getAttribute('aria-label'), document.querySelector('[data-page="orders"]'), document.querySelector('.layout-topbar')].should.deep.equal(['Place order', null, null])); - it('should derive the dialog from the slice', () => [screen.getByRole('heading', { name: 'Place order' }).textContent, document.querySelector('.scene-slice-dialog-subtitle')!.textContent].should.deep.equal(['Place order', 'State Change · PlaceOrder'])); + it('should derive the dialog from the slice', () => [screen.getByRole('heading', { name: 'Place order' }).textContent, document.querySelector('[data-scene-slice-dialog]')!.getAttribute('data-scene-command')].should.deep.equal(['Place order', 'PlaceOrder'])); it('should fill the fields from the active sample data pack', () => [field('Customer').value, field('Product').value, field('Quantity').value].should.deep.equal(['Fjordline AS', 'Scanner', '4'])); it('should report the slice in the breadcrumb', () => breadcrumbs.should.deep.equal([['Sales', 'Orders', 'Place order']])); it('should have a Cancel button and a button titled like the slice', () => [screen.getByRole('button', { name: 'Cancel' }).textContent, document.querySelector('.scene-slice-dialog-submit')!.textContent].should.deep.equal(['Cancel', 'Place order'])); diff --git a/Source/JavaScript/blueprint.default/shell/layout.css b/Source/JavaScript/blueprint.default/shell/layout.css index 8f96e8b..84eb19c 100644 --- a/Source/JavaScript/blueprint.default/shell/layout.css +++ b/Source/JavaScript/blueprint.default/shell/layout.css @@ -1198,13 +1198,6 @@ font-size: 1.35rem; } -.scene-slice-dialog-subtitle { - margin: 0.25rem 0 0; - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.8rem; - color: var(--scene-text-muted-color, #6b7280); -} - .scene-slice-dialog-fields { display: flex; flex-direction: column; diff --git a/Source/JavaScript/react/for_SliceDialog/when_a_toolbar_button_opens_a_slice_dialog.tsx b/Source/JavaScript/react/for_SliceDialog/when_a_toolbar_button_opens_a_slice_dialog.tsx index 4594ed3..5f5bb3a 100644 --- a/Source/JavaScript/react/for_SliceDialog/when_a_toolbar_button_opens_a_slice_dialog.tsx +++ b/Source/JavaScript/react/for_SliceDialog/when_a_toolbar_button_opens_a_slice_dialog.tsx @@ -34,8 +34,8 @@ describe('when a toolbar button opens a slice as a task dialog', () => { it('should open a modal dialog named by the button title', () => [dialog()!.getAttribute('aria-modal'), dialog()!.getAttribute('aria-label')].should.deep.equal(['true', 'New order'])); - it('should show the title and the slice as the subtitle', () => - [screen.getByRole('heading', { name: 'New order' }).textContent, document.querySelector('.scene-slice-dialog-subtitle')!.textContent].should.deep.equal(['New order', 'State Change · PlaceOrder'])); + it('should show the title', () => screen.getByRole('heading', { name: 'New order' }).textContent!.should.equal('New order')); + it('should not show what the slice is called in the model', () => (document.querySelector('.scene-slice-dialog')!.textContent ?? '').should.not.contain('State Change')); it('should show a labelled field for each command property', () => Array.from(document.querySelectorAll('.scene-slice-dialog-field label')).map(label => label.textContent).should.deep.equal(['Customer *', 'Product', 'Quantity', 'Express delivery'])); it('should focus the first field', () => document.activeElement!.getAttribute('name')!.should.equal('customer')); diff --git a/Source/JavaScript/react/slices/SliceDialogView.tsx b/Source/JavaScript/react/slices/SliceDialogView.tsx index dada10a..226b275 100644 --- a/Source/JavaScript/react/slices/SliceDialogView.tsx +++ b/Source/JavaScript/react/slices/SliceDialogView.tsx @@ -2,9 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. import { FormEvent, KeyboardEvent, useContext, useEffect, useId, useMemo, useRef, useState } from 'react'; -import { ApplicationStructureIndex } from '@cratis/scene.engine'; import { SceneNavigationContextInternal } from '../navigation/SceneNavigationContext'; -import { useSceneNavigationModel } from '../navigation/region/SceneNavigationModelContext'; import { InvocationResult } from './InvocationResult'; import { InvocationValidationError } from './InvocationValidationError'; import { SliceCommandField } from './SliceCommandField'; @@ -70,14 +68,15 @@ export function slicePayload(fields: SliceCommandField[], values: Record fieldsFor?.(command, slice) ?? [], [command, fieldsFor, slice]); - const sliceName = useMemo(() => { - if (!model) return slice.split('.').pop() ?? slice; - const index = model.structure instanceof ApplicationStructureIndex ? model.structure : new ApplicationStructureIndex(model.structure); - return index.findSlice(slice)?.id ?? slice.split('.').pop() ?? slice; - }, [model, slice]); const [values, setValues] = useState>(() => initialValuesOf(fields)); const [errors, setErrors] = useState([]); @@ -161,7 +154,6 @@ export function SliceDialogView({ slice: sliceProp, command: commandProp, title: return

{title}

-

State Change · {sliceName}

{fields.map(field => {