@processmaker/screen-builder 2.83.10 → 2.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/main.js CHANGED
@@ -225,8 +225,7 @@ window.ProcessMaker = {
225
225
  }
226
226
  },
227
227
  alert(message, variant) {
228
- variant;
229
- message;
228
+ console.log(`${variant}: ${message}`);
230
229
  },
231
230
  screen: {
232
231
  cacheEnabled: cacheEnabled ? cacheEnabled.content === "true" : false,
@@ -24,7 +24,7 @@ export default {
24
24
  if (json instanceof Array) {
25
25
  screen = { config:json, computed: [], customCSS: null };
26
26
  } else if (json && json.screens instanceof Array) {
27
- screen = json.screens[1];
27
+ screen = json.screens[0];
28
28
  if (window.exampleScreens instanceof Array) {
29
29
  window.exampleScreens = json.screens;
30
30
  }
@@ -0,0 +1,79 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+ import { userEvent, expect } from "@storybook/test";
3
+ import "../bootstrap";
4
+ import ColorSelect from "../components/inspector/color-select.vue";
5
+
6
+ export default {
7
+ title: "Components/ColorSelect",
8
+ component: ColorSelect,
9
+ tags: ["autodocs"],
10
+ render: (args, { argTypes }) => ({
11
+ props: Object.keys(argTypes),
12
+ components: { ColorSelect },
13
+ template: '<color-select v-bind="$props" v-model="inputValue" />',
14
+ data() {
15
+ return { inputValue: args.value };
16
+ },
17
+ watch: {
18
+ // Updates the value when the property changes in storybook controls
19
+ value(value) {
20
+ this.inputValue = value;
21
+ }
22
+ }
23
+ })
24
+ };
25
+
26
+ /**
27
+ * Stories of the component
28
+ */
29
+ // Preview the component
30
+ export const Preview = {
31
+ args: {
32
+ label: "Select a color",
33
+ helper: "Helper text",
34
+ options: [
35
+ { value: "alert alert-primary", content: "primary" },
36
+ { value: "alert alert-secondary", content: "secondary" },
37
+ { value: "alert alert-success", content: "success" },
38
+ { value: "alert alert-danger", content: "danger" },
39
+ { value: "alert alert-warning", content: "warning" },
40
+ { value: "alert alert-info", content: "info" },
41
+ { value: "alert alert-light", content: "light" },
42
+ { value: "alert alert-dark", content: "dark" }
43
+ ],
44
+ value: "alert alert-success"
45
+ }
46
+ };
47
+
48
+ // A user can change the selected color
49
+ export const ChangeSelectedColor = {
50
+ args: {
51
+ label: "Select a color",
52
+ helper: "Helper text",
53
+ options: [
54
+ { value: "alert alert-primary", content: "primary" },
55
+ { value: "alert alert-secondary", content: "secondary" },
56
+ { value: "alert alert-success", content: "success" },
57
+ { value: "alert alert-danger", content: "danger" },
58
+ { value: "alert alert-warning", content: "warning" },
59
+ { value: "alert alert-info", content: "info" },
60
+ { value: "alert alert-light", content: "light" },
61
+ { value: "alert alert-dark", content: "dark" }
62
+ ],
63
+ value: "alert alert-success"
64
+ },
65
+ play: async ({ canvasElement }) => {
66
+ const primaryColor = canvasElement.querySelector(".text-primary.fa-check");
67
+
68
+ // check if the default value is the success color
69
+ let selected = canvasElement.querySelector(".text-light");
70
+ expect(selected.parentNode).toHaveClass("bg-success");
71
+
72
+ // change the selected color
73
+ await userEvent.click(primaryColor);
74
+
75
+ // check if the alert is now the primary color
76
+ selected = canvasElement.querySelector(".text-light");
77
+ expect(selected.parentNode).toHaveClass("bg-primary");
78
+ }
79
+ };
@@ -0,0 +1,78 @@
1
+ import pkg from '../../package.json'; // Adjust the path as necessary
2
+ import { Meta } from "@storybook/blocks";
3
+
4
+ export const RightArrow = () => <svg
5
+ viewBox="0 0 14 14"
6
+ width="8px"
7
+ height="14px"
8
+ style={{
9
+ marginLeft: '4px',
10
+ display: 'inline-block',
11
+ shapeRendering: 'inherit',
12
+ verticalAlign: 'middle',
13
+ fill: 'currentColor',
14
+ 'path fill': 'currentColor'
15
+ }}
16
+ >
17
+ <path d="m11.1 7.35-5.5 5.5a.5.5 0 0 1-.7-.7L10.04 7 4.9 1.85a.5.5 0 1 1 .7-.7l5.5 5.5c.2.2.2.5 0 .7Z" />
18
+ </svg>
19
+
20
+ <Meta title="ProcessMaker ScreenBuilder" />
21
+
22
+ # ScreenBuilder <strong>v{pkg.version}</strong>
23
+
24
+ <div className="sb-container">
25
+ <div className='sb-section-title'>
26
+ ## Introduction
27
+ </div>
28
+
29
+ Welcome to the ProcessMaker ScreenBuilder Storybook! This interactive library is designed to showcase the components used to render screens within ProcessMaker processes. Built with Vue2, our components provide a versatile and intuitive way to build dynamic forms and interfaces for your business processes.
30
+
31
+ <div className='sb-section-title'>
32
+ ## Key Features
33
+ </div>
34
+ <ul>
35
+ <li>**Wide Range of Components**: From basic input fields to complex data grids, our library covers all the components you might need.</li>
36
+ <li>**Vue2 Compatibility**: Fully compatible with Vue2, ensuring seamless integration into your Vue applications.</li>
37
+ <li>**Interactive Examples**: Explore components through interactive examples, allowing you to see how they work in real-time.</li>
38
+ <li>**Customization**: Learn how to customize components to match your specific process requirements.</li>
39
+ </ul>
40
+
41
+ <div className='sb-section-title'>
42
+ ## Getting Started
43
+ </div>
44
+
45
+ To start using the ScreenBuilder components in your ProcessMaker processes, follow these steps:
46
+
47
+ Clone the repository and `cd` into the `screen-builder` directory:
48
+
49
+ ```bash
50
+ git clone git@github.com:ProcessMaker/screen-builder.git
51
+ cd screen-builder
52
+ ```
53
+
54
+ Install dependencies using NPM, then run the local development server:
55
+
56
+ ```bash
57
+ npm i
58
+ npm run serve
59
+ ```
60
+ <div className='sb-section-title'>
61
+ ## Explore Components
62
+ </div>
63
+ Start exploring the components by selecting them from the sidebar. Each entry provides detailed information about the component, including usage examples and customization options.
64
+ </div>
65
+
66
+ <style>
67
+ {`
68
+ .sb-container {
69
+ font-family: 'Arial', sans-serif;
70
+ }
71
+
72
+ .sb-section-title {
73
+ margin-top: 20px;
74
+ font-weight: bold;
75
+ font-size: 20px;
76
+ }
77
+ `}
78
+ </style>
@@ -0,0 +1,112 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+ import { within, expect, waitFor } from "@storybook/test";
3
+ import "../bootstrap";
4
+ // b-tabs from bootstrap-vue
5
+ import TabsBar from "../components/TabsBar.vue";
6
+ import PagesDropdown from "../components/editor/pagesDropdown.vue";
7
+
8
+ export default {
9
+ title: "Components/DropdownAndPages",
10
+ component: [TabsBar, PagesDropdown],
11
+ tags: ["autodocs"],
12
+ render: (args, { argTypes }) => ({
13
+ props: Object.keys(argTypes),
14
+ components: { TabsBar, PagesDropdown },
15
+ template: `
16
+ <tabs-bar ref="tabsBar" v-bind="$props">
17
+ <template v-slot:tabs-start>
18
+ <pages-dropdown
19
+ :data="pages"
20
+ @addPage="onAddPage"
21
+ @clickPage="onClick"
22
+ @seeAllPages="onSeeAllPages"
23
+ />
24
+ </template>
25
+ <template v-slot="{ currentPage }">
26
+ Here comes content of {{pages[currentPage].name}} (#{{currentPage}})
27
+ </template>
28
+ </tabs-bar>
29
+ `,
30
+ data() {
31
+ return {};
32
+ },
33
+ methods: {
34
+ onAddPage() {
35
+ console.log("Add page clicked");
36
+ },
37
+ onSeeAllPages() {
38
+ console.log("See all pages clicked");
39
+ },
40
+ onClick(index) {
41
+ this.$refs.tabsBar.openPageByIndex(index);
42
+ }
43
+ }
44
+ })
45
+ };
46
+
47
+ /**
48
+ * Stories of the component
49
+ */
50
+ // Preview the component
51
+ export const Preview = {
52
+ args: {
53
+ pages: [
54
+ { name: "Page 1" },
55
+ { name: "Page 2" },
56
+ { name: "Page 3" },
57
+ { name: "Page 4" },
58
+ { name: "Page 5" }
59
+ ],
60
+ initialOpenedPages: [0]
61
+ }
62
+ };
63
+
64
+ // Open a page using the PageDropdown(index)
65
+ export const OpenPageUsingDropdown = {
66
+ args: {
67
+ pages: [
68
+ { name: "Page1" },
69
+ { name: "Page2" },
70
+ { name: "Page3" },
71
+ { name: "Page4" },
72
+ { name: "Page5" }
73
+ ],
74
+ initialOpenedPages: [0]
75
+ },
76
+ play: async ({ canvasElement, step }) => {
77
+ const canvas = within(canvasElement);
78
+ const selector = canvasElement.querySelector(
79
+ "[data-test=page-dropdown] button"
80
+ );
81
+ let selectorAddPage = canvasElement.querySelector("[data-test=page-Page3]");
82
+ console.log(selectorAddPage);
83
+ await selector.click(selector);
84
+ await selectorAddPage.click(selectorAddPage);
85
+ // Open Page 3 (index=2)
86
+ await step("Open Page 3 (index=2)", async () => {
87
+ await waitFor(
88
+ () => {
89
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
90
+ "Here comes content of Page3 (#2)"
91
+ );
92
+ },
93
+ { timeout: 1000 }
94
+ );
95
+ });
96
+
97
+ // Open Page 2 (index=1)
98
+ await selector.click(selector);
99
+ selectorAddPage = canvasElement.querySelector("[data-test=page-Page2]");
100
+ await selectorAddPage.click(selectorAddPage);
101
+ await step("Open Page 2 (index=1)", async () => {
102
+ await waitFor(
103
+ () => {
104
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
105
+ "Here comes content of Page2 (#1)"
106
+ );
107
+ },
108
+ { timeout: 1000 }
109
+ );
110
+ });
111
+ }
112
+ };
@@ -0,0 +1,338 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+ import { within, userEvent, expect, waitFor } from "@storybook/test";
3
+ import "../bootstrap";
4
+ // b-tabs from bootstrap-vue
5
+ import TabsBar from "../components/TabsBar.vue";
6
+
7
+ export default {
8
+ title: "Components/TabsBar",
9
+ component: TabsBar,
10
+ tags: ["autodocs"],
11
+ render: (args, { argTypes }) => ({
12
+ props: Object.keys(argTypes),
13
+ components: { TabsBar },
14
+ template: `
15
+ <tabs-bar ref="tabsBar" v-bind="$props">
16
+ <template v-slot:tabs-start>
17
+ <b-form-select :options="pages.map((v,k)=>k)" @change="openPage($event)" data-test="open-page" />
18
+ </template>
19
+ <template v-slot="{ currentPage }">
20
+ Here comes content of {{pages[currentPage].name}} (#{{currentPage}})
21
+ </template>
22
+ </tabs-bar>
23
+ `,
24
+ data() {
25
+ return {};
26
+ },
27
+ methods: {
28
+ openPage(index) {
29
+ this.$refs.tabsBar.openPageByIndex(index);
30
+ }
31
+ }
32
+ })
33
+ };
34
+
35
+ /**
36
+ * Stories of the component
37
+ */
38
+ // Preview the component
39
+ export const Preview = {
40
+ args: {
41
+ pages: [
42
+ { name: "Page 1" },
43
+ { name: "Page 2" },
44
+ { name: "Page 3" },
45
+ { name: "Page 4" },
46
+ { name: "Page 5" }
47
+ ],
48
+ initialOpenedPages: [0]
49
+ }
50
+ };
51
+
52
+ // Open a page using openPageByIndex(index)
53
+ export const OpenPageByIndexFunction = {
54
+ args: {
55
+ pages: [
56
+ { name: "Page 1" },
57
+ { name: "Page 2" },
58
+ { name: "Page 3" },
59
+ { name: "Page 4" },
60
+ { name: "Page 5" }
61
+ ],
62
+ initialOpenedPages: [0]
63
+ },
64
+ play: async ({ canvasElement, step }) => {
65
+ const canvas = within(canvasElement);
66
+ const selector = canvasElement.querySelector("[data-test=open-page]");
67
+
68
+ // Open Page 3 (index=2)
69
+ await step("Open Page 3 (index=2)", async () => {
70
+ userEvent.selectOptions(selector, "2");
71
+ await waitFor(
72
+ () => {
73
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
74
+ "Here comes content of Page 3 (#2)"
75
+ );
76
+ },
77
+ { timeout: 1000 }
78
+ );
79
+ });
80
+
81
+ // Open Page 2 (index=1)
82
+ await step("Open Page 2 (index=1)", async () => {
83
+ userEvent.selectOptions(selector, "1");
84
+ await waitFor(
85
+ () => {
86
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
87
+ "Here comes content of Page 2 (#1)"
88
+ );
89
+ },
90
+ { timeout: 1000 }
91
+ );
92
+ });
93
+
94
+ // Close Tab #1 = Page 3 (index=2)
95
+ await step("Close Page 3 (index=2)", async () => {
96
+ canvas.getByTestId("close-tab-2").click();
97
+ await waitFor(
98
+ () => {
99
+ expect(canvas.getByTestId("tab-content")).not.toContainHTML(
100
+ "Here comes content of Page 3 (#2)"
101
+ );
102
+ },
103
+ { timeout: 1000 }
104
+ );
105
+ });
106
+
107
+ // Select Page 1 using dropdown (index=0) (tab=0)
108
+ await step("Select Page 1 (index=0)", async () => {
109
+ userEvent.selectOptions(selector, "0");
110
+ await waitFor(
111
+ () => {
112
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
113
+ "Here comes content of Page 1 (#0)"
114
+ );
115
+ },
116
+ { timeout: 1000 }
117
+ );
118
+ });
119
+
120
+ // Select Page 2 using dropdown (index=1) (tab=1)
121
+ await step("Select Page 2 (index=1)", async () => {
122
+ userEvent.selectOptions(selector, "1");
123
+ await waitFor(
124
+ () => {
125
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
126
+ "Here comes content of Page 2 (#1)"
127
+ );
128
+ },
129
+ { timeout: 1000 }
130
+ );
131
+ });
132
+ }
133
+ };
134
+
135
+ // User navigating through tabs
136
+ export const UserNavigatingThroughTabs = {
137
+ args: {
138
+ pages: [
139
+ { name: "Page 1" },
140
+ { name: "Page 2" },
141
+ { name: "Page 3" },
142
+ { name: "Page 4" },
143
+ { name: "Page 5" }
144
+ ],
145
+ initialOpenedPages: [0, 1]
146
+ },
147
+ play: async ({ canvasElement, step }) => {
148
+ const canvas = within(canvasElement);
149
+
150
+ // Select Page 2 using tab (data-test=tab-1)
151
+ await step("Select Page 2 using tab", async () => {
152
+ canvas.getByTestId("tab-1").click();
153
+ await waitFor(
154
+ () => {
155
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
156
+ "Here comes content of Page 2 (#1)"
157
+ );
158
+ },
159
+ { timeout: 1000 }
160
+ );
161
+ });
162
+
163
+ // Select Page 1 using tab (data-test=tab-0)
164
+ await step("Select Page 1 using tab", async () => {
165
+ canvas.getByTestId("tab-0").click();
166
+ await waitFor(
167
+ () => {
168
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
169
+ "Here comes content of Page 1 (#0)"
170
+ );
171
+ },
172
+ { timeout: 1000 }
173
+ );
174
+ });
175
+ }
176
+ };
177
+
178
+ // Lots of pages opened
179
+ export const ALotOfPagesOpen = {
180
+ args: {
181
+ pages: [
182
+ { name: "Page 1" },
183
+ { name: "Page 2" },
184
+ { name: "Page 3" },
185
+ { name: "Page 4" },
186
+ { name: "Page 5" },
187
+ { name: "Page 6" },
188
+ { name: "Page 7" },
189
+ { name: "Page 8" },
190
+ { name: "Page 9" },
191
+ { name: "Page 10" },
192
+ { name: "Page 11" },
193
+ { name: "Page 12" },
194
+ { name: "Page 13" },
195
+ { name: "Page 14" },
196
+ { name: "Page 15" },
197
+ { name: "Page 16" }
198
+ ],
199
+ initialOpenedPages: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
200
+ },
201
+ play: async ({ canvasElement, step }) => {
202
+ const canvas = within(canvasElement);
203
+ const scrollLeft = canvas.getByTestId("scroll-left");
204
+ const scrollRight = canvas.getByTestId("scroll-right");
205
+
206
+ await waitFor(() => expect(scrollRight).toBeVisible());
207
+ await waitFor(() => expect(scrollLeft).toBeVisible());
208
+
209
+ // Scroll to the right
210
+ await step("Scroll to the right", async () => {
211
+ scrollRight.click();
212
+ await waitFor(() => expect(scrollLeft).toBeVisible());
213
+ });
214
+
215
+ // Scroll to the left
216
+ await step("Scroll to the left", async () => {
217
+ scrollLeft.click();
218
+ await waitFor(() => expect(scrollLeft).toBeVisible());
219
+ });
220
+ }
221
+ };
222
+
223
+ // Tab content fill all the available space
224
+ export const TabContentFillAllTheAvailableSpace = {
225
+ args: {
226
+ pages: [
227
+ { name: "Page 1" },
228
+ { name: "Page 2" },
229
+ { name: "Page 3" },
230
+ { name: "Page 4" },
231
+ { name: "Page 5" }
232
+ ],
233
+ initialOpenedPages: [0, 1]
234
+ },
235
+ parameters: {
236
+ layout: "fullscreen"
237
+ },
238
+ decorators: [
239
+ () => ({
240
+ template: '<div style="height: calc(100vh - 2rem)"><story/></div>'
241
+ })
242
+ ],
243
+ play: async ({ canvasElement, step }) => {
244
+ const canvas = within(canvasElement);
245
+
246
+ // Check content position in Page 1
247
+ await step("Check content position in Page 1", async () => {
248
+ const tabContent = canvas.getByTestId("tab-content");
249
+ const tabsBar = canvas.getByRole("tablist").parentElement;
250
+
251
+ // -------------------------------------
252
+ //
253
+ // Check the height of the tab content is the same as the height
254
+ // of the canvas minus height of the tabs bar. This is important
255
+ // to make sure the tab content fill all the available space and
256
+ // the dropzone can be used to drop elements. Also check the top
257
+ // position of the tab content is the same as the height of the
258
+ // tabs bar.
259
+ //
260
+ // -------------------------------------
261
+ await waitFor(() => {
262
+ const canvasHeight = canvasElement.clientHeight;
263
+ const tabsBarHeight = tabsBar.clientHeight;
264
+ const tabContentTop = tabContent.getBoundingClientRect().top;
265
+ expect(tabContentTop).toBe(tabsBarHeight);
266
+ expect(tabContent).toHaveStyle({
267
+ height: `${canvasHeight - tabsBarHeight}px`
268
+ });
269
+ });
270
+ });
271
+
272
+ // Select Page 2 using tab (data-test=tab-1)
273
+ await step("Select Page 2 using tab", async () => {
274
+ canvas.getByTestId("tab-1").click();
275
+ await waitFor(
276
+ () => {
277
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
278
+ "Here comes content of Page 2 (#1)"
279
+ );
280
+ },
281
+ { timeout: 1000 }
282
+ );
283
+ });
284
+
285
+ // Check content position in Page 2
286
+ await step("Check content position in Page 2", async () => {
287
+ const tabContent = canvas.getByTestId("tab-content");
288
+ const tabsBar = canvas.getByRole("tablist").parentElement;
289
+
290
+ // -------------------------------------
291
+ //
292
+ // Check the height of the tab content is the same as the height
293
+ // of the canvas minus height of the tabs bar. This is important
294
+ // to make sure the tab content fill all the available space and
295
+ // the dropzone can be used to drop elements. Also check the top
296
+ // position of the tab content is the same as the height of the
297
+ // tabs bar.
298
+ //
299
+ // -------------------------------------
300
+ await waitFor(() => {
301
+ const canvasHeight = canvasElement.clientHeight;
302
+ const tabsBarHeight = tabsBar.clientHeight;
303
+ const tabContentTop = tabContent.getBoundingClientRect().top;
304
+ expect(tabContentTop).toBe(tabsBarHeight);
305
+ expect(tabContent).toHaveStyle({
306
+ height: `${canvasHeight - tabsBarHeight}px`
307
+ });
308
+ });
309
+ });
310
+ }
311
+ };
312
+
313
+ // Without any page opened
314
+ export const WithoutAnyPageOpened = {
315
+ args: {
316
+ pages: [
317
+ { name: "Page 1" },
318
+ { name: "Page 2" },
319
+ { name: "Page 3" },
320
+ { name: "Page 4" },
321
+ { name: "Page 5" }
322
+ ],
323
+ initialOpenedPages: []
324
+ },
325
+ play: async ({ canvasElement }) => {
326
+ const canvas = within(canvasElement);
327
+
328
+ // Check that there is a message when there is no page open.
329
+ await waitFor(
330
+ () => {
331
+ expect(canvas.getByTestId("tab-content")).toContainHTML(
332
+ "There are no open pages."
333
+ );
334
+ },
335
+ { timeout: 1000 }
336
+ );
337
+ }
338
+ };