Skip to content

Commit

Permalink
feat(wizards/subfunction): add create wizard (#733)
Browse files Browse the repository at this point in the history
* feat(wizards/subfunction): add create wizard

* feat(editors/substation/sub-function-editor): add add button
  • Loading branch information
JakobVogelsang committed May 19, 2022
1 parent a4abf4a commit 53067de
Show file tree
Hide file tree
Showing 13 changed files with 703 additions and 7 deletions.
61 changes: 60 additions & 1 deletion src/editors/substation/function-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,30 @@ import {
customElement,
state,
css,
query,
} from 'lit-element';

import '../../action-pane.js';
import './sub-function-editor.js';
import { getChildElementsByTagName } from '../../foundation.js';
import {
getChildElementsByTagName,
newWizardEvent,
SCLTag,
tags,
} from '../../foundation.js';
import { translate } from 'lit-translate';
import { emptyWizard, wizards } from '../../wizards/wizard-library.js';
import { Menu } from '@material/mwc-menu';
import { ListItem } from '@material/mwc-list/mwc-list-item';
import { IconButton } from '@material/mwc-icon-button';

function childTags(element: Element | null | undefined): SCLTag[] {
if (!element) return [];

return tags[<SCLTag>element.tagName].children.filter(
child => wizards[child].create !== emptyWizard
);
}

/** Pane rendering `Function` element with its children */
@customElement('function-editor')
Expand All @@ -27,6 +46,19 @@ export class FunctionEditor extends LitElement {
return `${name}${desc ? ` - ${desc}` : ''}${type ? ` (${type})` : ''}`;
}

@query('mwc-menu') addMenu!: Menu;
@query('mwc-icon-button[icon="playlist_add"]') addButton!: IconButton;

private openCreateWizard(tagName: string): void {
const wizard = wizards[<SCLTag>tagName].create(this.element!);

if (wizard) this.dispatchEvent(newWizardEvent(wizard));
}

firstUpdated(): void {
this.addMenu.anchor = <HTMLElement>this.addButton;
}

private renderLNodes(): TemplateResult {
const lNodes = getChildElementsByTagName(this.element, 'LNode');

Expand All @@ -49,12 +81,39 @@ export class FunctionEditor extends LitElement {
)}`;
}

private renderAddButtons(): TemplateResult[] {
return childTags(this.element).map(
child =>
html`<mwc-list-item value="${child}"
><span>${child}</span></mwc-list-item
>`
);
}

render(): TemplateResult {
return html`<action-pane
label="${this.header}"
icon="functions"
secondary
highlighted
><abbr
slot="action"
style="position:relative;"
title="${translate('add')}"
>
<mwc-icon-button
icon="playlist_add"
@click=${() => (this.addMenu.open = true)}
></mwc-icon-button
><mwc-menu
corner="BOTTOM_RIGHT"
menuCorner="END"
@selected=${(e: Event) => {
const tagName = (<ListItem>(<Menu>e.target).selected).value;
this.openCreateWizard(tagName);
}}
>${this.renderAddButtons()}</mwc-menu
> </abbr
>${this.renderLNodes()}${this.renderSubFunctions()}</action-pane
>`;
}
Expand Down
66 changes: 65 additions & 1 deletion src/editors/substation/sub-function-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,34 @@ import {
customElement,
state,
css,
query,
} from 'lit-element';
import { translate } from 'lit-translate';

import '@material/mwc-icon-button';
import '@material/mwc-list/mwc-list-item';
import '@material/mwc-menu';
import { IconButton } from '@material/mwc-icon-button';
import { ListItem } from '@material/mwc-list/mwc-list-item';
import { Menu } from '@material/mwc-menu';

import '../../action-pane.js';
import { getChildElementsByTagName } from '../../foundation.js';
import './sub-function-editor.js';
import {
getChildElementsByTagName,
newWizardEvent,
SCLTag,
tags,
} from '../../foundation.js';
import { emptyWizard, wizards } from '../../wizards/wizard-library.js';

function childTags(element: Element | null | undefined): SCLTag[] {
if (!element) return [];

return tags[<SCLTag>element.tagName].children.filter(
child => wizards[child].create !== emptyWizard
);
}

/** Pane rendering `SubFunction` element with its children */
@customElement('sub-function-editor')
Expand All @@ -26,6 +50,19 @@ export class SubFunctionEditor extends LitElement {
return `${name}${desc ? ` - ${desc}` : ''}${type ? ` (${type})` : ''}`;
}

@query('mwc-menu') addMenu!: Menu;
@query('mwc-icon-button[icon="playlist_add"]') addButton!: IconButton;

private openCreateWizard(tagName: string): void {
const wizard = wizards[<SCLTag>tagName].create(this.element!);

if (wizard) this.dispatchEvent(newWizardEvent(wizard));
}

firstUpdated(): void {
this.addMenu.anchor = <HTMLElement>this.addButton;
}

private renderLNodes(): TemplateResult {
const lNodes = getChildElementsByTagName(this.element, 'LNode');

Expand All @@ -48,8 +85,35 @@ export class SubFunctionEditor extends LitElement {
)}`;
}

private renderAddButtons(): TemplateResult[] {
return childTags(this.element).map(
child =>
html`<mwc-list-item value="${child}"
><span>${child}</span></mwc-list-item
>`
);
}

render(): TemplateResult {
return html`<action-pane label="${this.header}" icon="functions" secondary
><abbr
slot="action"
style="position:relative;"
title="${translate('add')}"
>
<mwc-icon-button
icon="playlist_add"
@click=${() => (this.addMenu.open = true)}
></mwc-icon-button
><mwc-menu
corner="BOTTOM_RIGHT"
menuCorner="END"
@selected=${(e: Event) => {
const tagName = (<ListItem>(<Menu>e.target).selected).value;
this.openCreateWizard(tagName);
}}
>${this.renderAddButtons()}</mwc-menu
> </abbr
>${this.renderLNodes()}${this.renderSubFunctions()}</action-pane
>`;
}
Expand Down
4 changes: 3 additions & 1 deletion src/wizards/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ interface ContentOptions {
reservedNames: string[];
}

function contentFunctionWizard(content: ContentOptions): TemplateResult[] {
export function contentFunctionWizard(
content: ContentOptions
): TemplateResult[] {
return [
html`<wizard-textfield
label="name"
Expand Down
56 changes: 56 additions & 0 deletions src/wizards/subfunction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { get } from 'lit-translate';

import {
createElement,
getValue,
Wizard,
WizardActor,
WizardInputElement,
} from '../foundation.js';
import { contentFunctionWizard } from './function.js';

function createSubFunctionAction(parent: Element): WizardActor {
return (inputs: WizardInputElement[]) => {
const subFunctionAttrs: Record<string, string | null> = {};
const subFunctionKeys = ['name', 'desc', 'type'];
subFunctionKeys.forEach(key => {
subFunctionAttrs[key] = getValue(inputs.find(i => i.label === key)!);
});

const subFunction = createElement(
parent.ownerDocument,
'SubFunction',
subFunctionAttrs
);

return [{ new: { parent, element: subFunction } }];
};
}

export function createSubFunctionWizard(parent: Element): Wizard {
const name = '';
const desc = null;
const type = null;
const reservedNames = Array.from(parent.querySelectorAll('SubFunction')).map(
fUnction => fUnction.getAttribute('name')!
);

return [
{
title: get('wizard.title.add', { tagName: 'SubFunction' }),
primary: {
icon: 'save',
label: get('save'),
action: createSubFunctionAction(parent),
},
content: [
...contentFunctionWizard({
name,
desc,
type,
reservedNames,
}),
],
},
];
}
3 changes: 2 additions & 1 deletion src/wizards/wizard-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { createDaWizard } from './da.js';
import { editDAIWizard } from './dai.js';
import { editGseControlWizard } from './gsecontrol.js';
import { createFunctionWizard } from './function.js';
import { createSubFunctionWizard } from './subfunction.js';

type SclElementWizard = (
element: Element,
Expand Down Expand Up @@ -477,7 +478,7 @@ export const wizards: Record<
},
SubFunction: {
edit: emptyWizard,
create: emptyWizard,
create: createSubFunctionWizard,
},
SubNetwork: {
edit: editSubNetworkWizard,
Expand Down
87 changes: 87 additions & 0 deletions test/integration/editors/substation/function-editor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { fixture, html, expect } from '@open-wc/testing';

import '../../../mock-wizard-editor.js';
import { MockWizardEditor } from '../../../mock-wizard-editor.js';

import '../../../../src/editors/substation/function-editor.js';
import { FunctionEditor } from '../../../../src/editors/substation/function-editor.js';
import { WizardTextField } from '../../../../src/wizard-textfield.js';

describe('function-editor wizarding editing integration', () => {
describe('open create wizard for element SubFunction', () => {
let doc: XMLDocument;
let parent: MockWizardEditor;
let element: FunctionEditor | null;

let nameField: WizardTextField;
let primaryAction: HTMLElement;

beforeEach(async () => {
doc = await fetch('/test/testfiles/zeroline/functions.scd')
.then(response => response.text())
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
parent = <MockWizardEditor>(
await fixture(
html`<mock-wizard-editor
><function-editor
.element=${doc.querySelector('Function')}
></function-editor
></mock-wizard-editor>`
)
);

element = parent.querySelector('function-editor');

(<HTMLElement>(
element?.shadowRoot?.querySelector('mwc-list-item[value="SubFunction"]')
)).click();
await parent.updateComplete;

nameField = <WizardTextField>(
parent.wizardUI.dialog?.querySelector('wizard-textfield[label="name"]')
);

primaryAction = <HTMLElement>(
parent.wizardUI.dialog?.querySelector(
'mwc-button[slot="primaryAction"]'
)
);
});

it('does not add SubFunction if name attribute is not unique', async () => {
expect(
doc.querySelector(
'Substation > Function > SubFunction[name="mySubFunc"]'
)
).to.exist;

nameField.value = 'mySubFunc';
primaryAction.click();
await parent.updateComplete;

expect(
doc.querySelectorAll(
'Substation > Function > SubFunction[name="mySubFunc"]'
).length
).to.equal(1);
});

it('does add SubFunction if name attribute is unique', async () => {
expect(
doc.querySelector(
'Substation > Function > SubFunction[name="someNewFunction"]'
)
).to.not.exist;

nameField.value = 'someNewFunction';
await parent.updateComplete;
primaryAction.click();

expect(
doc.querySelector(
'Substation > Function > SubFunction[name="someNewFunction"]'
)
).to.exist;
});
});
});
Loading

0 comments on commit 53067de

Please sign in to comment.