Skip to content

Commit 84660e3

Browse files
committed
feat: wizard tabs
1 parent de5b332 commit 84660e3

File tree

9 files changed

+160
-51
lines changed

9 files changed

+160
-51
lines changed

src/webview/WizardFormBuilder.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import { ErrorMessages, Rules, TypeCheckingRule } from 'validatorjs';
2-
import { Wizard, WizardField } from './types';
2+
import { Wizard, WizardTab } from './types';
33

44
export class WizardFormBuilder {
55
private title?: string;
66
private description?: string;
7-
private fields: WizardField[] = [];
7+
private tabs: WizardTab[] = [];
88
private validation: Rules = {};
99
private validationMessages: ErrorMessages = {};
1010

1111
public static new(): WizardFormBuilder {
1212
return new WizardFormBuilder();
1313
}
1414

15+
public addTab(tab: WizardTab): void {
16+
this.tabs.push(tab);
17+
}
18+
1519
public setTitle(title: string): void {
1620
this.title = title;
1721
}
@@ -20,10 +24,6 @@ export class WizardFormBuilder {
2024
this.description = description;
2125
}
2226

23-
public addField(field: WizardField): void {
24-
this.fields.push(field);
25-
}
26-
2727
public addValidation(
2828
field: string,
2929
rule: string | Array<string | TypeCheckingRule> | Rules
@@ -40,14 +40,14 @@ export class WizardFormBuilder {
4040
throw new Error('Title is required');
4141
}
4242

43-
if (!this.fields.length) {
44-
throw new Error('Fields are required');
43+
if (!this.tabs.length) {
44+
throw new Error('Tabs are required');
4545
}
4646

4747
return {
4848
title: this.title,
4949
description: this.description,
50-
fields: this.fields,
50+
tabs: this.tabs,
5151
validation: this.validation,
5252
validationMessages: this.validationMessages,
5353
};

src/webview/WizardTabBuilder.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { WizardField, WizardTab } from './types';
2+
3+
export class WizardTabBuilder {
4+
private id?: string;
5+
private title?: string;
6+
private description?: string;
7+
private fields: WizardField[] = [];
8+
9+
public static new(): WizardTabBuilder {
10+
return new WizardTabBuilder();
11+
}
12+
13+
public setId(id: string): void {
14+
this.id = id;
15+
}
16+
17+
public setTitle(title: string): void {
18+
this.title = title;
19+
}
20+
21+
public setDescription(description: string): void {
22+
this.description = description;
23+
}
24+
25+
public addField(field: WizardField): void {
26+
this.fields.push(field);
27+
}
28+
29+
public build(): WizardTab {
30+
if (!this.id) {
31+
throw new Error('Id is required');
32+
}
33+
34+
if (!this.title) {
35+
throw new Error('Title is required');
36+
}
37+
38+
if (!this.fields.length) {
39+
throw new Error('Fields are required');
40+
}
41+
42+
return {
43+
id: this.id,
44+
title: this.title,
45+
description: this.description,
46+
fields: this.fields,
47+
};
48+
}
49+
}

src/webview/components/Wizard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const Wizard: React.FC<WizardProps> = ({ data, vscode }) => {
1212
<div>
1313
<h1>{data.title}</h1>
1414
<p>{data.description}</p>
15-
<vscode-divider></vscode-divider>
15+
<br />
1616
<Renderer wizard={data} vscode={vscode} />
1717
</div>
1818
);

src/webview/components/Wizard/Renderer.tsx

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Form, Formik, FormikValues } from 'formik';
1+
import { Form, Formik, FormikProps, FormikValues } from 'formik';
22
import { useCallback } from 'react';
33
import { WebviewApi } from 'vscode-webview';
44
import Validator from 'validatorjs';
@@ -11,13 +11,17 @@ interface Props {
1111
}
1212

1313
export const Renderer: React.FC<Props> = ({ wizard, vscode }) => {
14-
const initialValues: FormikValues = wizard.fields.reduce((acc: FormikValues, field) => {
15-
if (field.type === WizardInput.Select && field.multiple) {
16-
acc[field.id] = field.initialValue ?? [];
14+
const initialValues: FormikValues = wizard.tabs.reduce((acc: FormikValues, tab) => {
15+
tab.fields.reduce((acc: FormikValues, field) => {
16+
if (field.type === WizardInput.Select && field.multiple) {
17+
acc[field.id] = field.initialValue ?? [];
18+
return acc;
19+
}
20+
21+
acc[field.id] = field.initialValue ?? '';
1722
return acc;
18-
}
23+
}, {});
1924

20-
acc[field.id] = field.initialValue ?? '';
2125
return acc;
2226
}, {});
2327

@@ -27,7 +31,6 @@ export const Renderer: React.FC<Props> = ({ wizard, vscode }) => {
2731

2832
const handleValidation = useCallback((values: FormikValues) => {
2933
if (wizard.validation) {
30-
console.log(wizard.validation);
3134
const validation = new Validator(values, wizard.validation, wizard.validationMessages);
3235

3336
validation.passes();
@@ -39,16 +42,34 @@ export const Renderer: React.FC<Props> = ({ wizard, vscode }) => {
3942
}, []);
4043

4144
return (
42-
<main className="p-6 w-full">
45+
<main>
4346
<Formik initialValues={initialValues} onSubmit={handleSubmit} validate={handleValidation}>
44-
<Form>
45-
{wizard.fields.map(field => {
46-
return <FieldRenderer key={field.id} field={field} />;
47-
})}
48-
<div className="mt-4 flex gap-4">
49-
<vscode-button type="submit">Submit</vscode-button>
50-
</div>
51-
</Form>
47+
{(props: FormikProps<any>) => {
48+
return (
49+
<>
50+
<vscode-tabs>
51+
{wizard.tabs.map(tab => {
52+
return (
53+
<>
54+
<vscode-tab-header slot="header">{tab.title}</vscode-tab-header>
55+
<vscode-tab-panel>
56+
<p>{tab.description}</p>
57+
<br />
58+
{tab.fields.map(field => {
59+
return <FieldRenderer key={field.id} field={field} />;
60+
})}
61+
</vscode-tab-panel>
62+
</>
63+
);
64+
})}
65+
</vscode-tabs>
66+
<br />
67+
<vscode-button disabled={!props.dirty || !props.isValid} type="submit">
68+
Submit
69+
</vscode-button>
70+
</>
71+
);
72+
}}
5273
</Formik>
5374
</main>
5475
);

src/webview/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,19 @@ export type WizardMessage = Message<Wizard>;
2020
export interface Wizard {
2121
title: string;
2222
description?: string;
23-
fields: WizardField[];
23+
tabs: WizardTab[];
2424
validationSchema?: any;
2525
validation?: Rules;
2626
validationMessages?: ErrorMessages;
2727
}
2828

29+
export interface WizardTab {
30+
id: string;
31+
title: string;
32+
description?: string;
33+
fields: WizardField[];
34+
}
35+
2936
export type WizardField =
3037
| WizardTextField
3138
| WizardNumberField

src/wizard/BlockWizard.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import ModuleIndexer from 'indexer/module/ModuleIndexer';
33
import { GeneratorWizard } from 'webview/GeneratorWizard';
44
import { WizardFieldBuilder } from 'webview/WizardFieldBuilder';
55
import { WizardFormBuilder } from 'webview/WizardFormBuilder';
6+
import { WizardTabBuilder } from 'webview/WizardTabBuilder';
67

78
export interface BlockWizardData {
89
module: string;
@@ -25,19 +26,25 @@ export default class BlockWizard extends GeneratorWizard {
2526
builder.setTitle('Generate a new block');
2627
builder.setDescription('Generates a new Magento2 block class.');
2728

28-
builder.addField(WizardFieldBuilder.select('module', 'Module*').setOptions(modules).build());
29+
const tab = new WizardTabBuilder();
30+
tab.setId('block');
31+
tab.setTitle('Block');
2932

30-
builder.addField(
33+
tab.addField(WizardFieldBuilder.select('module', 'Module*').setOptions(modules).build());
34+
35+
tab.addField(
3136
WizardFieldBuilder.text('name', 'Block Name*').setPlaceholder('Block class name').build()
3237
);
3338

34-
builder.addField(
39+
tab.addField(
3540
WizardFieldBuilder.text('path', 'Block Directory*')
3641
.setPlaceholder('Block/Path')
3742
.setInitialValue('Block')
3843
.build()
3944
);
4045

46+
builder.addTab(tab.build());
47+
4148
builder.addValidation('module', 'required');
4249
builder.addValidation('name', 'required|min:1');
4350
builder.addValidation('path', 'required|min:1');

src/wizard/ModuleWizard.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { License } from 'types';
44
import { GeneratorWizard } from 'webview/GeneratorWizard';
55
import { WizardFieldBuilder } from 'webview/WizardFieldBuilder';
66
import { WizardFormBuilder } from 'webview/WizardFormBuilder';
7+
import { WizardTabBuilder } from 'webview/WizardTabBuilder';
78

89
interface ModuleWizardBaseData {
910
vendor: string;
@@ -40,21 +41,26 @@ export default class ModuleWizard extends GeneratorWizard {
4041
builder.setTitle('Generate a new module');
4142
builder.setDescription('Generates the basic structure of a Magento2 module.');
4243

43-
builder.addField(
44+
const tab = new WizardTabBuilder();
45+
tab.setId('module');
46+
tab.setTitle('Module');
47+
48+
tab.addField(
4449
WizardFieldBuilder.text('vendor', 'Vendor*').setPlaceholder('Vendor name').build()
4550
);
4651

47-
builder.addField(
52+
tab.addField(
4853
WizardFieldBuilder.text('module', 'Module*').setPlaceholder('Module name').build()
4954
);
5055

51-
builder.addField(
56+
tab.addField(
5257
WizardFieldBuilder.select('sequence', 'Dependencies')
5358
.setOptions(modules)
5459
.setMultiple(true)
5560
.build()
5661
);
57-
builder.addField(
62+
63+
tab.addField(
5864
WizardFieldBuilder.select('license', 'License')
5965
.setOptions([
6066
{ label: 'No license', value: 'none' },
@@ -65,28 +71,32 @@ export default class ModuleWizard extends GeneratorWizard {
6571
])
6672
.build()
6773
);
68-
builder.addField(
74+
75+
tab.addField(
6976
WizardFieldBuilder.text('version', 'Version')
7077
.setPlaceholder('Version')
7178
.setInitialValue('1.0.0')
7279
.build()
7380
);
74-
builder.addField(WizardFieldBuilder.checkbox('composer', 'Generate composer.json').build());
7581

76-
builder.addField(
82+
tab.addField(WizardFieldBuilder.checkbox('composer', 'Generate composer.json').build());
83+
84+
tab.addField(
7785
WizardFieldBuilder.text('composerName', 'Package name')
7886
.setPlaceholder('module/name')
7987
.addDependsOn('composer', true)
8088
.build()
8189
);
8290

83-
builder.addField(
91+
tab.addField(
8492
WizardFieldBuilder.text('composerDescription', 'Package description')
8593
.setPlaceholder('Package description')
8694
.addDependsOn('composer', true)
8795
.build()
8896
);
8997

98+
builder.addTab(tab.build());
99+
90100
builder.addValidation('vendor', 'required|min:1');
91101
builder.addValidation('module', 'required|min:1');
92102
builder.addValidation('composerName', [{ required_if: ['composer', true] }]);

src/wizard/ObserverWizard.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { MagentoScope } from 'types';
55
import { GeneratorWizard } from 'webview/GeneratorWizard';
66
import { WizardFieldBuilder } from 'webview/WizardFieldBuilder';
77
import { WizardFormBuilder } from 'webview/WizardFormBuilder';
8+
import { WizardTabBuilder } from 'webview/WizardTabBuilder';
89

910
export interface ObserverWizardData {
1011
module: string;
@@ -30,43 +31,50 @@ export default class ObserverWizard extends GeneratorWizard {
3031
builder.setTitle('Generate a new observer');
3132
builder.setDescription('Generates a new observer.');
3233

33-
builder.addField(
34+
const tab = new WizardTabBuilder();
35+
tab.setId('observer');
36+
tab.setTitle('Observer');
37+
38+
tab.addField(
3439
WizardFieldBuilder.select('module', 'Module')
3540
.setDescription(['Module where observer will be generated in'])
3641
.setOptions(modules)
3742
.setInitialValue(modules[0].value)
3843
.build()
3944
);
4045

41-
builder.addField(
46+
tab.addField(
4247
WizardFieldBuilder.select('area', 'Area')
4348
.setOptions(Object.values(MagentoScope).map(scope => ({ label: scope, value: scope })))
4449
.setInitialValue(MagentoScope.Global)
4550
.build()
4651
);
4752

48-
builder.addField(
53+
tab.addField(
4954
WizardFieldBuilder.text('eventName', 'Event name')
5055
.setPlaceholder('event_name')
5156
.setInitialValue(initialEventName)
5257
.build()
5358
);
54-
builder.addField(
59+
60+
tab.addField(
5561
WizardFieldBuilder.text('observerName', 'Observer name')
5662
.setPlaceholder('observer_name')
5763
.build()
5864
);
5965

60-
builder.addField(
66+
tab.addField(
6167
WizardFieldBuilder.text('className', 'Observer class name')
6268
.setPlaceholder('ObserverName')
6369
.build()
6470
);
6571

66-
builder.addField(
72+
tab.addField(
6773
WizardFieldBuilder.text('directoryPath', 'Directory path').setInitialValue('Observer').build()
6874
);
6975

76+
builder.addTab(tab.build());
77+
7078
builder.addValidation('module', 'required');
7179
builder.addValidation('area', 'required');
7280
builder.addValidation('eventName', [

0 commit comments

Comments
 (0)