Problem
Currently, the frontend manages state using independent boolean flags and loose ID properties. This makes it difficult to track valid UI states and forces unsafe as casting.
Proposed Strategy:
1. Unified Modal State (State Machine)
Bind the Mode and Data into a single discriminated union property.
type UIState =
| { mode: 'idle' }
| { mode: 'delete'; channel: NotificationChannelResponse }
| { mode: 'edit'; channel: NotificationChannelResponse }
| { mode: 'create' };
@state() private _uiState: UIState = { mode: 'idle' };
// Transitioning is atomic and type-safe!
private _handleDelete(channel: NotificationChannelResponse) {
this._uiState = { mode: 'delete', channel };
}
2. Registry Inversion (Direct Injection)
Move type-narrowing to the Registry. This allows forms to be "Dumb UI" components that simply receive the data they need.
// channel-config-registry.ts
case 'webhook': {
// Narrowing happens ONCE here
const config = channel?.config?.type === 'webhook' ? channel.config : undefined;
return html`
<webhook-config-form
.channelName=${channel?.name ?? ''}
.config=${config}
></webhook-config-form>`;
}
3. Clean Switch-based Rendering
Use a helper method to map state to templates. TypeScript automatically narrows this._uiState in each case.
render() {
return html`${this.renderModals()}`;
}
private renderModals() {
switch (this._uiState.mode) {
case 'delete':
// Narrowed: this._uiState.channel is guaranteed to exist
return html`
<sl-dialog open label="Delete ${this._uiState.channel.type} Channel">
Are you sure? This cannot be undone.
<sl-button slot="footer" @click=${this._confirmDelete}>Delete</sl-button>
</sl-dialog>`;
case 'edit':
return html`<edit-form .channel=${this._uiState.channel}></edit-form>`;
default:
return nothing;
}
}
Expected Outcomes
- Zero Casting: Eliminates
as unknown as WebhookConfig.
- Illegal State Prevention: You can't be in 'delete' mode without a channel.
- Improved Testability: Deterministic mapping from
_uiState value to DOM state.
Problem
Currently, the frontend manages state using independent boolean flags and loose ID properties. This makes it difficult to track valid UI states and forces unsafe
ascasting.Proposed Strategy:
1. Unified Modal State (State Machine)
Bind the Mode and Data into a single discriminated union property.
2. Registry Inversion (Direct Injection)
Move type-narrowing to the Registry. This allows forms to be "Dumb UI" components that simply receive the data they need.
3. Clean Switch-based Rendering
Use a helper method to map state to templates. TypeScript automatically narrows
this._uiStatein eachcase.Expected Outcomes
as unknown as WebhookConfig._uiStatevalue to DOM state.