@nyaruka/temba-components 0.52.1 → 0.53.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.
@@ -1 +1 @@
1
- {"version":3,"file":"temba-modax.test.js","sourceRoot":"","sources":["../../test/temba-modax.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { fixture, expect, assert } from '@open-wc/testing';\nimport { useFakeTimers } from 'sinon';\nimport { Button } from '../src/button/Button';\nimport { Modax } from '../src/dialog/Modax';\nimport { CustomEventType } from '../src/interfaces';\nimport { assertScreenshot, checkTimers, getClip, mockPOST } from './utils.test';\n\nlet clock: any;\n\nconst getModaxHTML = (endpoint: string): string => {\n return `\n <temba-modax header=\"Hello Modax\" endpoint=\"${endpoint}\">\n <div>Open Me</div>\n </temba-modax>\n `;\n};\n\nconst getButtons = (modax: Modax, type: string = null) => {\n return modax.shadowRoot\n .querySelector('temba-dialog')\n .shadowRoot.querySelectorAll(\n type ? `temba-button[${type}='']` : 'temba-button'\n );\n};\n\nconst open = async (modax: Modax) => {\n return new Promise((resolve: any, reject: any) => {\n modax.addEventListener(\n CustomEventType.Loaded,\n async (event: CustomEvent) => {\n await clock.runAll();\n resolve(event.detail);\n }\n );\n\n modax.addEventListener(\n CustomEventType.Redirected,\n async (event: CustomEvent) => {\n await clock.runAll();\n resolve(event.detail);\n }\n );\n\n modax.open = true;\n });\n};\n\nconst clickPrimary = async (modax: Modax) => {\n const buttons = getButtons(modax);\n\n if (buttons.length > 0) {\n let primary = buttons[0] as Button;\n\n if (buttons.length > 1) {\n // look for our primary flag\n buttons.forEach((button: Button) => {\n if (button.primary) {\n primary = button;\n }\n });\n }\n\n expect(primary).not.equals(undefined, 'Missing primary button');\n primary.click();\n }\n};\n\nconst getDialogClip = (modax: Modax) => {\n const dialog = modax.shadowRoot.querySelector('temba-dialog');\n return getClip(\n dialog.shadowRoot.querySelector('.dialog-container') as HTMLElement\n );\n};\n\ndescribe('temba-modax', () => {\n beforeEach(function () {\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('can be created', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/hello.html')\n );\n assert.instanceOf(modax, Modax);\n });\n\n it('opens', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/hello.html')\n );\n\n await open(modax);\n expect(modax.open).equals(true);\n\n // Now our body should have our endpoint text\n expect(modax.getBody().innerHTML).to.contain('Hello World');\n\n await assertScreenshot('modax/simple', getDialogClip(modax));\n });\n\n it('fetches forms', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/form.html')\n );\n expect(modax.open).to.equal(false);\n await open(modax);\n\n expect(modax.open).to.equal(true);\n\n expect(modax.primaryName).to.equal('Save Everything');\n expect(modax.cancelName).to.equal('Cancel');\n\n await assertScreenshot('modax/form', getDialogClip(modax));\n });\n\n it('reverts primary name on reuse', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/hello.html')\n );\n\n // await click('temba-modax');\n await open(modax);\n expect(modax.open).equals(true);\n\n // should only have one button, okay\n let buttons = getButtons(modax);\n expect(buttons.length).equals(1);\n\n // close our dialog\n await clickPrimary(modax);\n expect(modax.open).equals(false);\n\n // now fetch form from the same modax\n modax.endpoint = '/test-assets/modax/form.html';\n await open(modax);\n expect(modax.open).equals(true);\n\n // now we should have two buttons, 'Save Everything' and 'Cancel'\n buttons = getButtons(modax);\n expect(buttons.length).equals(2);\n\n // secondary should be Cancel, not Ok\n const secondary = getButtons(modax, 'secondary')[0] as Button;\n expect(secondary.name).equals('Cancel');\n });\n\n it('closes after redirect', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/form.html')\n );\n\n await open(modax);\n const primary = getButtons(modax, 'primary')[0] as Button;\n expect(primary.name).equals('Save Everything');\n\n // click the submit button\n mockPOST(/\\/test-assets\\/modax\\/form\\.html/, 'arst', {\n 'Temba-Success': 'hide',\n });\n\n const hideTest = new Promise<void>(resolve => {\n modax.addEventListener(CustomEventType.Submitted, () => {\n expect(modax.open).equals(false, 'Modal still visible');\n resolve();\n });\n });\n\n await clickPrimary(modax);\n await clock.runAllAsync();\n await hideTest;\n });\n});\n"]}
1
+ {"version":3,"file":"temba-modax.test.js","sourceRoot":"","sources":["../../test/temba-modax.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { fixture, expect, assert } from '@open-wc/testing';\nimport { useFakeTimers } from 'sinon';\nimport { Button } from '../src/button/Button';\nimport { Modax } from '../src/dialog/Modax';\nimport { CustomEventType } from '../src/interfaces';\nimport { assertScreenshot, getClip, mockPOST } from './utils.test';\n\nlet clock: any;\n\nconst getModaxHTML = (endpoint: string): string => {\n return `\n <temba-modax header=\"Hello Modax\" endpoint=\"${endpoint}\">\n <div>Open Me</div>\n </temba-modax>\n `;\n};\n\nconst getButtons = (modax: Modax, type: string = null) => {\n return modax.shadowRoot\n .querySelector('temba-dialog')\n .shadowRoot.querySelectorAll(\n type ? `temba-button[${type}='']` : 'temba-button'\n );\n};\n\nconst open = async (modax: Modax) => {\n return new Promise((resolve: any, reject: any) => {\n modax.addEventListener(\n CustomEventType.Loaded,\n async (event: CustomEvent) => {\n await clock.runAll();\n resolve(event.detail);\n }\n );\n\n modax.addEventListener(\n CustomEventType.Redirected,\n async (event: CustomEvent) => {\n await clock.runAll();\n resolve(event.detail);\n }\n );\n\n modax.open = true;\n });\n};\n\nconst clickPrimary = async (modax: Modax) => {\n const buttons = getButtons(modax);\n\n if (buttons.length > 0) {\n let primary = buttons[0] as Button;\n\n if (buttons.length > 1) {\n // look for our primary flag\n buttons.forEach((button: Button) => {\n if (button.primary) {\n primary = button;\n }\n });\n }\n\n expect(primary).not.equals(undefined, 'Missing primary button');\n primary.click();\n }\n};\n\nconst getDialogClip = (modax: Modax) => {\n const dialog = modax.shadowRoot.querySelector('temba-dialog');\n return getClip(\n dialog.shadowRoot.querySelector('.dialog-container') as HTMLElement\n );\n};\n\ndescribe('temba-modax', () => {\n beforeEach(function () {\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('can be created', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/hello.html')\n );\n assert.instanceOf(modax, Modax);\n });\n\n it('opens', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/hello.html')\n );\n\n await open(modax);\n expect(modax.open).equals(true);\n\n // Now our body should have our endpoint text\n expect(modax.getBody().innerHTML).to.contain('Hello World');\n\n await assertScreenshot('modax/simple', getDialogClip(modax));\n });\n\n it('fetches forms', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/form.html')\n );\n expect(modax.open).to.equal(false);\n await open(modax);\n\n expect(modax.open).to.equal(true);\n\n expect(modax.buttons[1].name).to.equal('Save Everything');\n expect(modax.buttons[0].name).to.equal('Cancel');\n\n await assertScreenshot('modax/form', getDialogClip(modax));\n });\n\n it('reverts primary name on reuse', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/hello.html')\n );\n\n // await click('temba-modax');\n await open(modax);\n expect(modax.open).equals(true);\n\n // should only have one button, okay\n let buttons = getButtons(modax);\n expect(buttons.length).equals(1);\n\n // close our dialog\n await clickPrimary(modax);\n expect(modax.open).equals(false);\n\n // now fetch form from the same modax\n modax.endpoint = '/test-assets/modax/form.html';\n await open(modax);\n expect(modax.open).equals(true);\n\n // now we should have two buttons, 'Save Everything' and 'Cancel'\n buttons = getButtons(modax);\n expect(buttons.length).equals(2);\n\n // secondary should be Cancel, not Ok\n const secondary = getButtons(modax, 'secondary')[0] as Button;\n expect(secondary.name).equals('Cancel');\n });\n\n it('closes after redirect', async () => {\n const modax: Modax = await fixture(\n getModaxHTML('/test-assets/modax/form.html')\n );\n\n await open(modax);\n const primary = getButtons(modax, 'primary')[0] as Button;\n expect(primary.name).equals('Save Everything');\n\n // click the submit button\n mockPOST(/\\/test-assets\\/modax\\/form\\.html/, 'arst', {\n 'Temba-Success': 'hide',\n });\n\n const hideTest = new Promise<void>(resolve => {\n modax.addEventListener(CustomEventType.Submitted, () => {\n expect(modax.open).equals(false, 'Modal still visible');\n resolve();\n });\n });\n\n await clickPrimary(modax);\n await clock.runAllAsync();\n await hideTest;\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.52.1",
3
+ "version": "0.53.0",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,63 @@
1
- import { LitElement } from 'lit';
1
+ import { LitElement, PropertyValueMap } from 'lit';
2
2
  import { CustomEventType } from './interfaces';
3
3
 
4
+ enum Color {
5
+ YELLOW = 33,
6
+ PURPLE = 35,
7
+ WHITE = 37,
8
+ BLUE = 34,
9
+ RED = 31,
10
+ CYAN = 36,
11
+ GREEN = 32,
12
+ BLACK = 30,
13
+ }
14
+
15
+ const colorize = (text: string, color: Color) => {
16
+ return `\x1b[${color}m${text}\x1b[0m`;
17
+ };
18
+
19
+ const tag = (ele: HTMLElement) => {
20
+ return colorize(ele.tagName.padEnd(30), Color.PURPLE);
21
+ };
22
+
23
+ const showUpdates = (
24
+ ele: HTMLElement,
25
+ changes: Map<PropertyKey, unknown>,
26
+ firstUpdated = false
27
+ ) => {
28
+ if (ele['DEBUG_UPDATES'] || ele['DEBUG']) {
29
+ if (changes.size > 0) {
30
+ console.log(
31
+ tag(ele),
32
+ firstUpdated
33
+ ? colorize('<updated>', Color.YELLOW)
34
+ : colorize('<first-updated>', Color.BLACK)
35
+ );
36
+ for (const [key, value] of changes.entries()) {
37
+ console.log(
38
+ ' ' + String(key).padEnd(30),
39
+ value,
40
+ colorize('=>', Color.WHITE),
41
+ ele[key]
42
+ );
43
+ }
44
+ }
45
+ }
46
+ };
47
+
48
+ const showEvent = (ele: HTMLElement, type: string, details = undefined) => {
49
+ if (ele['DEBUG_EVENTS'] || ele['DEBUG']) {
50
+ console.log(
51
+ tag(ele),
52
+ details !== undefined
53
+ ? colorize('<custom-event>', Color.RED)
54
+ : colorize('<event> ', Color.CYAN),
55
+ colorize(type, Color.BLUE),
56
+ details !== undefined ? details : ''
57
+ );
58
+ }
59
+ };
60
+
4
61
  export interface EventHandler {
5
62
  event: string;
6
63
  method: EventListener;
@@ -8,6 +65,10 @@ export interface EventHandler {
8
65
  }
9
66
 
10
67
  export class RapidElement extends LitElement {
68
+ DEBUG = false;
69
+ DEBUG_UPDATES = false;
70
+ DEBUG_EVENTS = false;
71
+
11
72
  private eles: { [selector: string]: HTMLDivElement } = {};
12
73
  public getEventHandlers(): EventHandler[] {
13
74
  return [];
@@ -36,7 +97,23 @@ export class RapidElement extends LitElement {
36
97
  super.disconnectedCallback();
37
98
  }
38
99
 
100
+ protected firstUpdated(
101
+ changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
102
+ ): void {
103
+ super.firstUpdated(changes);
104
+ showUpdates(this, changes, true);
105
+ }
106
+
107
+ protected updated(
108
+ changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
109
+ ): void {
110
+ super.updated(changes);
111
+ showUpdates(this, changes, false);
112
+ }
113
+
39
114
  public fireEvent(type: string): any {
115
+ showEvent(this, type);
116
+
40
117
  return this.dispatchEvent(
41
118
  new Event(type, {
42
119
  bubbles: true,
@@ -46,6 +123,10 @@ export class RapidElement extends LitElement {
46
123
  }
47
124
 
48
125
  public fireCustomEvent(type: CustomEventType, detail: any = {}): any {
126
+ if (this['DEBUG_EVENTS']) {
127
+ showEvent(this, type, detail);
128
+ }
129
+
49
130
  const event = new CustomEvent(type, {
50
131
  detail,
51
132
  bubbles: true,
@@ -184,6 +184,9 @@ export class Button extends LitElement {
184
184
  @property({ type: String })
185
185
  href: string;
186
186
 
187
+ @property({ type: Number })
188
+ index?: number;
189
+
187
190
  private handleClick(evt: MouseEvent) {
188
191
  if (this.disabled) {
189
192
  evt.preventDefault();
@@ -1,11 +1,24 @@
1
1
  import { property } from 'lit/decorators.js';
2
- import { TemplateResult, html, css } from 'lit';
2
+ import { TemplateResult, html, css, PropertyValueMap } from 'lit';
3
3
  import { Button } from '../button/Button';
4
4
  import { RapidElement } from '../RapidElement';
5
5
  import { CustomEventType } from '../interfaces';
6
6
  import { styleMap } from 'lit-html/directives/style-map.js';
7
7
  import { getClasses } from '../utils';
8
8
 
9
+ export enum ButtonType {
10
+ PRIMARY = 'primary',
11
+ SECONDARY = 'secondary',
12
+ DESTRUCTIVE = 'destructive',
13
+ }
14
+ export class DialogButton {
15
+ name?: string;
16
+ id?: string;
17
+ details?: any;
18
+ type?: string;
19
+ closes?: boolean;
20
+ }
21
+
9
22
  export class Dialog extends RapidElement {
10
23
  static get widths(): { [size: string]: string } {
11
24
  return {
@@ -24,6 +37,10 @@ export class Dialog extends RapidElement {
24
37
  background: white;
25
38
  }
26
39
 
40
+ .flex-grow {
41
+ flex-grow: 1;
42
+ }
43
+
27
44
  .flex {
28
45
  display: flex;
29
46
  flex-direction: column;
@@ -99,6 +116,9 @@ export class Dialog extends RapidElement {
99
116
  }
100
117
 
101
118
  .header-text {
119
+ display: flex;
120
+ flex-direction: row;
121
+ align-items: center;
102
122
  font-size: 20px;
103
123
  padding: 12px 20px;
104
124
  font-weight: 300;
@@ -106,11 +126,21 @@ export class Dialog extends RapidElement {
106
126
  background: var(--header-bg);
107
127
  }
108
128
 
129
+ .header-text .title {
130
+ flex-grow: 1;
131
+ }
132
+
133
+ .header-text .status {
134
+ font-size: 0.6em;
135
+ font-weight: bold;
136
+ }
137
+
109
138
  .dialog-footer {
110
139
  background: var(--color-primary-light);
111
140
  padding: 10px;
112
141
  display: flex;
113
- flex-flow: row-reverse;
142
+ flex-flow: row;
143
+ align-items: center;
114
144
  }
115
145
 
116
146
  temba-button {
@@ -198,6 +228,9 @@ export class Dialog extends RapidElement {
198
228
  @property()
199
229
  ready: boolean;
200
230
 
231
+ @property({ type: Array })
232
+ buttons: DialogButton[] = [];
233
+
201
234
  @property({ attribute: false })
202
235
  onButtonClicked: (button: Button) => void;
203
236
 
@@ -207,6 +240,25 @@ export class Dialog extends RapidElement {
207
240
  super();
208
241
  }
209
242
 
243
+ protected firstUpdated(
244
+ changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
245
+ ): void {
246
+ if (changes.has('cancelButtonName') && this.cancelButtonName) {
247
+ this.buttons.push({
248
+ name: this.cancelButtonName,
249
+ type: ButtonType.SECONDARY,
250
+ closes: true,
251
+ });
252
+ }
253
+
254
+ if (changes.has('primaryButtonName') && this.primaryButtonName) {
255
+ this.buttons.push({
256
+ name: this.primaryButtonName,
257
+ type: ButtonType.PRIMARY,
258
+ });
259
+ }
260
+ }
261
+
210
262
  public updated(changedProperties: Map<string, any>) {
211
263
  if (changedProperties.has('open')) {
212
264
  const body = document.querySelector('body');
@@ -270,8 +322,13 @@ export class Dialog extends RapidElement {
270
322
  public handleClick(evt: MouseEvent) {
271
323
  const button = evt.currentTarget as Button;
272
324
  if (!button.disabled) {
273
- this.fireCustomEvent(CustomEventType.ButtonClicked, { button });
274
- if (button.name === this.cancelButtonName) {
325
+ let detail: DialogButton = {};
326
+ if (button.index >= 0 && button.index < this.buttons.length) {
327
+ detail = this.buttons[button.index];
328
+ }
329
+
330
+ this.fireCustomEvent(CustomEventType.ButtonClicked, { button, detail });
331
+ if (button.name === this.cancelButtonName || (detail && detail.closes)) {
275
332
  this.open = false;
276
333
  }
277
334
  }
@@ -343,7 +400,9 @@ export class Dialog extends RapidElement {
343
400
  const header = this.header
344
401
  ? html`
345
402
  <div class="dialog-header">
346
- <div class="header-text">${this.header}</div>
403
+ <div class="header-text">
404
+ <div class="title">${this.header}</div>
405
+ </div>
347
406
  </div>
348
407
  `
349
408
  : null;
@@ -382,26 +441,23 @@ export class Dialog extends RapidElement {
382
441
  </div>
383
442
 
384
443
  <div class="dialog-footer">
385
- ${
386
- this.primaryButtonName
387
- ? html`
388
- <temba-button
389
- @click=${this.handleClick}
390
- .name=${this.primaryButtonName}
391
- ?destructive=${this.destructive}
392
- ?primary=${!this.destructive}
393
- ?submitting=${this.submitting}
394
- ?disabled=${this.disabled}
395
- >}</temba-button
396
- >
397
- `
398
- : null
399
- }
400
- <temba-button
401
- @click=${this.handleClick}
402
- name=${this.cancelButtonName}
403
- secondary
404
- ></temba-button>
444
+ <div class="flex-grow">
445
+ <slot name="gutter"></slot>
446
+ </div>
447
+ ${this.buttons.map(
448
+ (button: DialogButton, index) => html`
449
+ <temba-button
450
+ name=${button.name}
451
+ ?destructive=${button.type == 'primary' && this.destructive}
452
+ ?primary=${button.type == 'primary' && !this.destructive}
453
+ ?secondary=${button.type == 'secondary'}
454
+ ?submitting=${this.submitting}
455
+ ?disabled=${this.disabled}
456
+ index=${index}
457
+ @click=${this.handleClick}
458
+ ></temba-button>
459
+ `
460
+ )}
405
461
  </div>
406
462
  </div>
407
463
  <div class="grow-bottom"></div>
@@ -3,9 +3,9 @@ import { property } from 'lit/decorators.js';
3
3
  import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
4
4
 
5
5
  import { RapidElement } from '../RapidElement';
6
- import { getUrl, serialize, postUrl, WebResponse } from '../utils';
6
+ import { getUrl, serialize, postUrl, WebResponse, getClasses } from '../utils';
7
7
  import { CustomEventType } from '../interfaces';
8
- import { Dialog } from './Dialog';
8
+ import { ButtonType, Dialog, DialogButton } from './Dialog';
9
9
 
10
10
  export class Modax extends RapidElement {
11
11
  static get styles() {
@@ -24,6 +24,11 @@ export class Modax extends RapidElement {
24
24
  display: none;
25
25
  }
26
26
 
27
+ button[type='submit'],
28
+ input[type='submit'] {
29
+ display: none;
30
+ }
31
+
27
32
  .modax-body {
28
33
  padding: 20px;
29
34
  display: block;
@@ -67,6 +72,33 @@ export class Modax extends RapidElement {
67
72
  border-radius: 6px;
68
73
  font-weight: 300;
69
74
  }
75
+
76
+ .step-ball {
77
+ background: rgba(var(--primary-rgb), 0.2);
78
+ width: 1.2em;
79
+ height: 1.2em;
80
+ border-radius: 100%;
81
+ margin-right: 0.5em;
82
+ border: 0.15em solid transparent;
83
+ }
84
+
85
+ .step-ball.complete {
86
+ background: rgba(var(--primary-rgb), 0.7);
87
+ cursor: pointer;
88
+ }
89
+ .step-ball.complete:hover {
90
+ background: rgba(var(--primary-rgb), 0.8);
91
+ }
92
+
93
+ .step-ball.active {
94
+ border: 0.15em solid var(--color-primary-dark);
95
+ }
96
+
97
+ .wizard-steps {
98
+ display: flex;
99
+ flex-direction: row;
100
+ margin-left: 0.6em;
101
+ }
70
102
  `;
71
103
  }
72
104
 
@@ -106,6 +138,15 @@ export class Modax extends RapidElement {
106
138
  @property({ type: Boolean })
107
139
  disabled = false;
108
140
 
141
+ @property({ type: Array })
142
+ buttons: DialogButton[] = [];
143
+
144
+ @property({ type: Number })
145
+ wizardStep = 0;
146
+
147
+ @property({ type: Number })
148
+ wizardStepCount = 0;
149
+
109
150
  @property({ type: Boolean })
110
151
  suspendSubmit = false;
111
152
  // private cancelToken: CancelTokenSource;
@@ -149,18 +190,29 @@ export class Modax extends RapidElement {
149
190
  }
150
191
 
151
192
  public updatePrimaryButton(): void {
193
+ const wizard = this.shadowRoot.querySelector(
194
+ '#wizard-form'
195
+ ) as HTMLDivElement;
196
+ if (wizard) {
197
+ this.wizardStep = parseInt(wizard.dataset.step);
198
+ this.wizardStepCount = parseInt(wizard.dataset.steps);
199
+ }
200
+
152
201
  if (!this.noSubmit) {
153
202
  this.updateComplete.then(() => {
154
203
  const submitButton = this.shadowRoot.querySelector(
155
- "input[type='submit']"
204
+ "input[type='submit'],button[type='submit']"
156
205
  ) as any;
157
206
 
158
207
  if (submitButton) {
159
- this.primaryName = submitButton.value;
160
- this.cancelName = 'Cancel';
208
+ this.buttons = [
209
+ { type: ButtonType.SECONDARY, name: 'Cancel', closes: true },
210
+ { type: ButtonType.PRIMARY, name: submitButton.value },
211
+ ];
161
212
  } else {
162
- this.primaryName = null;
163
- this.cancelName = 'Ok';
213
+ this.buttons = [
214
+ { type: ButtonType.SECONDARY, name: 'Ok', closes: true },
215
+ ];
164
216
  }
165
217
  this.submitting = false;
166
218
  });
@@ -229,22 +281,37 @@ export class Modax extends RapidElement {
229
281
  this.body = this.getLoading();
230
282
  getUrl(this.endpoint, null, this.getHeaders()).then(
231
283
  (response: WebResponse) => {
232
- this.setBody(response.body);
233
- this.fetching = false;
234
- this.updateComplete.then(() => {
235
- this.updatePrimaryButton();
236
- this.fireCustomEvent(CustomEventType.Loaded, {
237
- body: this.getBody(),
284
+ // if it's a full page, breakout of the modal
285
+ if (response.body.indexOf('<!DOCTYPE HTML>') == 0) {
286
+ document.location = response.url;
287
+ } else {
288
+ this.setBody(response.body);
289
+ this.fetching = false;
290
+ this.updateComplete.then(() => {
291
+ this.updatePrimaryButton();
292
+ this.fireCustomEvent(CustomEventType.Loaded, {
293
+ body: this.getBody(),
294
+ });
238
295
  });
239
- });
296
+ }
240
297
  }
241
298
  );
242
299
  }
243
300
 
244
- public submit(): void {
301
+ public submit(extra = {}): void {
245
302
  this.submitting = true;
246
303
  const form = this.shadowRoot.querySelector('form');
247
- const postData = form ? serialize(form) : {};
304
+
305
+ let postData = form ? serialize(form) : '';
306
+ if (extra) {
307
+ Object.keys(extra).forEach(key => {
308
+ postData +=
309
+ (postData.length > 1 ? '&' : '') +
310
+ encodeURIComponent(key) +
311
+ '=' +
312
+ encodeURIComponent(extra[key]);
313
+ });
314
+ }
248
315
 
249
316
  postUrl(
250
317
  this.endpoint,
@@ -278,7 +345,9 @@ export class Modax extends RapidElement {
278
345
  } else {
279
346
  // if we set the body, update our submit button
280
347
  if (this.setBody(response.body)) {
281
- this.updatePrimaryButton();
348
+ this.updateComplete.then(() => {
349
+ this.updatePrimaryButton();
350
+ });
282
351
  }
283
352
  }
284
353
  }, 1000);
@@ -290,15 +359,16 @@ export class Modax extends RapidElement {
290
359
 
291
360
  private handleDialogClick(evt: CustomEvent) {
292
361
  const button = evt.detail.button;
362
+ const detail = evt.detail.detail;
293
363
  if (!button.disabled && !button.submitting) {
294
- if (button.name === this.primaryName) {
364
+ if (button.primary || button.destructive) {
295
365
  if (!this.suspendSubmit) {
296
366
  this.submit();
297
367
  }
298
368
  }
299
369
  }
300
370
 
301
- if (button.name === (this.cancelName || 'Cancel')) {
371
+ if (detail.closes) {
302
372
  this.open = false;
303
373
  this.fetching = false;
304
374
  this.cancelName = undefined;
@@ -315,16 +385,47 @@ export class Modax extends RapidElement {
315
385
  return this.endpoint && this.endpoint.indexOf('delete') > -1;
316
386
  }
317
387
 
388
+ private handleGotoStep(evt: MouseEvent) {
389
+ const step = (evt.target as HTMLDivElement).dataset.gotoStep;
390
+ if (step) {
391
+ this.submit({ wizard_goto_step: step });
392
+ }
393
+ }
394
+
318
395
  public getBody() {
319
396
  return this.shadowRoot.querySelector('.modax-body');
320
397
  }
321
398
 
322
399
  public render(): TemplateResult {
400
+ const wizardStepBalls = [];
401
+
402
+ const wizard = this.shadowRoot.querySelector(
403
+ '#wizard-form'
404
+ ) as HTMLDivElement;
405
+ if (wizard) {
406
+ const completed = (wizard.getAttribute('data-completed') || '')
407
+ .split(',')
408
+ .filter(step => step.length > 0);
409
+
410
+ for (let i = 0; i < this.wizardStepCount; i++) {
411
+ wizardStepBalls.push(
412
+ html`<div
413
+ data-goto-step=${completed[i]}
414
+ @click=${this.handleGotoStep.bind(this)}
415
+ class="${getClasses({
416
+ 'step-ball': true,
417
+ active: this.wizardStep - 1 === i,
418
+ complete: i < completed.length,
419
+ })}"
420
+ ></div>`
421
+ );
422
+ }
423
+ }
424
+
323
425
  return html`
324
426
  <temba-dialog
325
- header=${this.header}
326
- .primaryButtonName=${this.noSubmit ? null : this.primaryName}
327
- .cancelButtonName=${this.cancelName || 'Cancel'}
427
+ .header=${this.header}
428
+ .buttons=${this.buttons}
328
429
  ?open=${this.open}
329
430
  ?loading=${this.fetching}
330
431
  ?submitting=${this.submitting}
@@ -338,6 +439,9 @@ export class Modax extends RapidElement {
338
439
  ${this.body}
339
440
  </div>
340
441
  <div class="scripts"></div>
442
+ <div slot="gutter">
443
+ <div class="wizard-steps">${wizardStepBalls}</div>
444
+ </div>
341
445
  </temba-dialog>
342
446
  <div class="slot-wrapper" @click=${this.handleSlotClicked}>
343
447
  <slot></slot>
@@ -85,6 +85,7 @@ export const getUrl = (
85
85
  controller,
86
86
  body,
87
87
  json,
88
+ url: response.url,
88
89
  headers: response.headers,
89
90
  status: response.status,
90
91
  });
@@ -1,5 +1,5 @@
1
1
  // for cache busting we dynamically generate a fingerprint, use yarn svg to update
2
- export const SVG_FINGERPRINT = '54daaf6fa5347c8e5a331b59d415d2bd';
2
+ export const SVG_FINGERPRINT = '5d22cc3a5fb06da9ea0a632539b39983';
3
3
 
4
4
  // only icons below are included in the sprite sheet
5
5
  export enum Icon {
@@ -116,6 +116,7 @@ export enum Icon {
116
116
  missing = 'maximize-02',
117
117
  missed_call = 'phone-x',
118
118
  new = 'plus',
119
+ next_schedule = 'alarm-clock',
119
120
  notification = 'bell-01',
120
121
  org_active = 'credit-card-02',
121
122
  org_anonymous = 'glasses-01',
@@ -129,6 +130,7 @@ export enum Icon {
129
130
  progress_spinner = 'refresh-cw-04',
130
131
  featured = 'star-01',
131
132
  referral = 'user-right-01',
133
+ resend = 'refresh-cw-05',
132
134
  resthooks = 'share-07',
133
135
  restore = 'play',
134
136
  retry = 'refresh-cw-05',