Skip to content

Commit a1edeef

Browse files
Global disable and various related changes.
1 parent c309a4d commit a1edeef

File tree

14 files changed

+283
-234
lines changed

14 files changed

+283
-234
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import * as React from "react";
2+
import { recordify, TypedRecord } from "typed-immutable-record";
3+
import { Iterable } from "immutable";
4+
5+
import { BaseContainer, BaseContainerProps } from "simplr-forms";
6+
import { FormError } from "simplr-forms/contracts";
7+
8+
export interface BaseFormButtonProps extends BaseContainerProps, React.HTMLProps<HTMLButtonElement> {
9+
disableOnError?: boolean;
10+
disableOnBusy?: boolean;
11+
disableOnPristine?: boolean;
12+
busy?: boolean;
13+
disabled?: boolean;
14+
busyClassName?: string;
15+
}
16+
17+
export interface BaseFormButtonState {
18+
HasError: boolean;
19+
Validating: boolean;
20+
Submitting: boolean;
21+
Pristine: boolean;
22+
Disabled: boolean;
23+
}
24+
25+
export interface BaseFormButtonStateRecord extends TypedRecord<BaseFormButtonStateRecord>, BaseFormButtonState { }
26+
27+
export abstract class BaseFormButton<TProps extends BaseFormButtonProps, TState extends BaseFormButtonStateRecord>
28+
extends BaseContainer<TProps, TState> {
29+
public static defaultProps: BaseFormButtonProps = {
30+
disableOnBusy: true,
31+
disableOnError: false,
32+
disableOnPristine: false,
33+
disabled: undefined,
34+
busyClassName: "busy"
35+
};
36+
37+
protected OnStoreUpdated(): void {
38+
const formStore = this.FormStore.GetState();
39+
const newState: BaseFormButtonState = {
40+
HasError: formStore.HasError,
41+
Validating: formStore.Validating,
42+
Submitting: formStore.Submitting,
43+
Pristine: formStore.Pristine,
44+
Disabled: formStore.Form.Disabled
45+
};
46+
47+
const newStateRecord = recordify<BaseFormButtonState, BaseFormButtonStateRecord>(newState);
48+
49+
// Type 'Readonly<TState>' cannot be converted to type 'Iterable<string, any>'.
50+
const stateIterable = this.state as any as Iterable<string, any>;
51+
if (!newStateRecord.equals(stateIterable)) {
52+
this.setState((prevState) => {
53+
// newStateRecord becomes an empty object after setState
54+
// This happens because of an underlying Immutable.Record
55+
// not enumerating properties in for..in
56+
return newState;
57+
});
58+
}
59+
}
60+
61+
protected get Disabled(): boolean {
62+
if (this.state == null) {
63+
return false;
64+
}
65+
66+
if (this.state.Disabled === true) {
67+
return true;
68+
}
69+
70+
const props = this.props as TProps;
71+
if (props.disabled != null) {
72+
return props.disabled;
73+
}
74+
75+
if (props.disableOnError === true &&
76+
this.state.HasError) {
77+
return true;
78+
}
79+
80+
if (props.disableOnBusy === true &&
81+
this.Busy) {
82+
return true;
83+
}
84+
85+
if (props.disableOnPristine === true &&
86+
this.state.Pristine === true) {
87+
return true;
88+
}
89+
return false;
90+
}
91+
92+
protected get Busy(): boolean {
93+
return this.props.busy === true ||
94+
this.state != null &&
95+
(this.state.Validating || this.state.Submitting);
96+
}
97+
98+
protected get InlineStyles(): React.CSSProperties {
99+
let inlineStyles: React.CSSProperties = {};
100+
const props = this.props as TProps;
101+
if (props.style != null) {
102+
inlineStyles = props.style;
103+
}
104+
105+
if (this.Busy && !props.disabled) {
106+
inlineStyles.cursor = "wait";
107+
}
108+
109+
return inlineStyles;
110+
}
111+
112+
protected get ClassName(): string | undefined {
113+
if (this.Busy) {
114+
return `${this.props.busyClassName} ${this.props.className}`;
115+
}
116+
return this.props.className;
117+
}
118+
119+
abstract render(): JSX.Element | null;
120+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./base-dom-field";
2+
export * from "./base-form-button";

packages/simplr-forms-dom/src/components/clear.tsx

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,22 @@ import { recordify, TypedRecord } from "typed-immutable-record";
33

44
import { BaseContainer, BaseContainerProps } from "simplr-forms";
55

6-
export interface ClearProps extends BaseContainerProps, React.HTMLProps<HTMLButtonElement> {
7-
fieldIds?: string[];
8-
}
6+
import {
7+
BaseFormButton,
8+
BaseFormButtonProps,
9+
BaseFormButtonStateRecord
10+
} from "../abstractions/base-form-button";
911

10-
export interface ClearState {
11-
Submitting: boolean;
12+
export interface ClearProps extends BaseFormButtonProps {
13+
fieldIds?: string[];
1214
}
1315

14-
export interface ClearStateRecord extends TypedRecord<ClearStateRecord>, ClearState { }
15-
16-
export class Clear extends BaseContainer<ClearProps, ClearStateRecord> {
17-
constructor(props: ClearProps) {
18-
super(props);
19-
20-
this.state = recordify<ClearState, ClearStateRecord>({
21-
Submitting: false
22-
});
23-
}
24-
25-
protected OnStoreUpdated(): void {
26-
const formStore = this.FormStore.GetState();
27-
const newState = recordify({
28-
Submitting: formStore.Form.Submitting
29-
});
30-
31-
if (!newState.equals(this.state)) {
32-
this.setState(() => newState);
33-
}
34-
}
35-
36-
protected get Disabled(): boolean {
37-
if (this.props.disabled != null) {
38-
return this.props.disabled;
39-
}
40-
41-
return this.state.Submitting;
42-
}
43-
16+
export class Clear extends BaseFormButton<ClearProps, BaseFormButtonStateRecord> {
4417
protected OnButtonClick: React.MouseEventHandler<HTMLButtonElement> = (event): void => {
45-
event.persist();
4618
this.FormStore.ClearFields(this.props.fieldIds);
4719

4820
if (this.props.onClick != null) {
21+
event.persist();
4922
this.props.onClick(event);
5023
}
5124
}
@@ -54,6 +27,8 @@ export class Clear extends BaseContainer<ClearProps, ClearStateRecord> {
5427
// TODO: Pass all other props.
5528
return <button
5629
type="button"
30+
className={this.ClassName}
31+
style={this.InlineStyles}
5732
disabled={this.Disabled}
5833
onClick={this.OnButtonClick}
5934
>

packages/simplr-forms-dom/src/components/reset.tsx

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,22 @@ import { recordify, TypedRecord } from "typed-immutable-record";
33

44
import { BaseContainer, BaseContainerProps } from "simplr-forms";
55

6-
export interface ResetProps extends BaseContainerProps, React.HTMLProps<HTMLButtonElement> {
7-
fieldIds?: string[];
8-
}
6+
import {
7+
BaseFormButton,
8+
BaseFormButtonProps,
9+
BaseFormButtonStateRecord
10+
} from "../abstractions/base-form-button";
911

10-
export interface ResetState {
11-
Submitting: boolean;
12+
export interface ResetProps extends BaseFormButtonProps {
13+
fieldIds?: string[];
1214
}
1315

14-
export interface ResetStateRecord extends TypedRecord<ResetStateRecord>, ResetState { }
15-
16-
export class Reset extends BaseContainer<ResetProps, ResetStateRecord> {
17-
constructor(props: ResetProps) {
18-
super(props);
19-
20-
this.state = recordify<ResetState, ResetStateRecord>({
21-
Submitting: false
22-
});
23-
}
24-
25-
protected OnStoreUpdated(): void {
26-
const formStore = this.FormStore.GetState();
27-
const newState = recordify({
28-
Submitting: formStore.Form.Submitting
29-
});
30-
31-
if (!newState.equals(this.state)) {
32-
this.setState(() => newState);
33-
}
34-
}
35-
36-
protected get Disabled(): boolean {
37-
if (this.props.disabled != null) {
38-
return this.props.disabled;
39-
}
40-
41-
return this.state.Submitting;
42-
}
43-
16+
export class Reset extends BaseFormButton<ResetProps, BaseFormButtonStateRecord> {
4417
protected OnButtonClick: React.MouseEventHandler<HTMLButtonElement> = (event): void => {
45-
event.persist();
4618
this.FormStore.ResetFields(this.props.fieldIds);
4719

4820
if (this.props.onClick != null) {
21+
event.persist();
4922
this.props.onClick(event);
5023
}
5124
}
@@ -54,6 +27,8 @@ export class Reset extends BaseContainer<ResetProps, ResetStateRecord> {
5427
// TODO: Pass all other props.
5528
return <button
5629
type="button"
30+
className={this.ClassName}
31+
style={this.InlineStyles}
5732
disabled={this.Disabled}
5833
onClick={this.OnButtonClick}
5934
>

packages/simplr-forms-dom/src/components/submit.tsx

Lines changed: 14 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -4,123 +4,25 @@ import { recordify, TypedRecord } from "typed-immutable-record";
44
import { BaseContainer, BaseContainerProps } from "simplr-forms";
55
import { FormError } from "simplr-forms/contracts";
66

7-
export interface SubmitProps extends BaseContainerProps, React.HTMLProps<HTMLButtonElement> {
8-
/**
9-
* Disable when at least one field has error.
10-
*
11-
* Default: true
12-
* @type {boolean}
13-
* @memberOf SubmitProps
14-
*/
15-
disableOnError?: boolean;
16-
/**
17-
* Disable when at least one field is validating.
18-
*
19-
* Default: true
20-
* @type {boolean}
21-
* @memberOf SubmitProps
22-
*/
23-
disableOnBusy?: boolean;
24-
/**
25-
* Disable when all fields are pristine.
26-
*
27-
* Default: false
28-
* @type {boolean}
29-
* @memberOf SubmitProps
30-
*/
31-
disableOnPristine?: boolean;
32-
busy?: boolean;
33-
busyClass?: string;
34-
}
35-
36-
export interface SubmitState {
37-
Error?: FormError;
38-
Validating: boolean;
39-
Submitting: boolean;
40-
Pristine: boolean;
41-
}
42-
43-
export interface SubmitStateRecord extends TypedRecord<SubmitStateRecord>, SubmitState { }
44-
45-
export class Submit extends BaseContainer<SubmitProps, SubmitStateRecord> {
46-
public static defaultProps: SubmitProps = {
47-
disableOnBusy: true,
48-
disableOnError: true,
49-
disableOnPristine: false,
50-
busyClass: "busy"
7+
import {
8+
BaseFormButton,
9+
BaseFormButtonProps,
10+
BaseFormButtonStateRecord
11+
} from "../abstractions/base-form-button";
12+
13+
export interface SubmitProps extends BaseFormButtonProps { }
14+
15+
export class Submit extends BaseFormButton<SubmitProps, BaseFormButtonStateRecord> {
16+
public static defaultProps: BaseFormButtonProps = {
17+
...BaseFormButton.defaultProps,
18+
disableOnError: true
5119
};
52-
53-
protected OnStoreUpdated(): void {
54-
const formStore = this.FormStore.GetState();
55-
const newState = {
56-
Error: formStore.Form.Error,
57-
Validating: formStore.Form.Validating,
58-
Submitting: formStore.Form.Submitting,
59-
Pristine: formStore.Pristine
60-
};
61-
62-
const newStateRecord = recordify(newState);
63-
if (!newStateRecord.equals(this.state)) {
64-
this.setState((prevState) => {
65-
// newStateRecord becomes an empty object after setState
66-
// This happens because of an underlying Immutable.Record
67-
return newState;
68-
});
69-
}
70-
}
71-
72-
protected get Disabled(): boolean {
73-
if (this.props.disabled != null) {
74-
return this.props.disabled;
75-
}
76-
if (this.state != null) {
77-
if (this.props.disableOnError === true &&
78-
this.state.Error != null) {
79-
console.log("Disabling submit on error.");
80-
return true;
81-
}
82-
83-
if (this.props.disableOnBusy === true &&
84-
this.Busy) {
85-
console.log("Disabling submit on busy.");
86-
return true;
87-
}
88-
89-
if (this.props.disableOnPristine === true &&
90-
this.state.Pristine === true) {
91-
return true;
92-
}
93-
}
94-
95-
return false;
96-
}
97-
98-
protected get Busy(): boolean {
99-
return this.props.busy === true ||
100-
this.state != null &&
101-
(this.state.Validating ||
102-
this.state.Submitting);
103-
}
104-
105-
protected get InlineStyles(): React.CSSProperties {
106-
let inlineStyles: React.CSSProperties = {};
107-
108-
if (this.props.style != null) {
109-
inlineStyles = this.props.style;
110-
}
111-
112-
if (this.Busy && !this.props.disabled) {
113-
inlineStyles.cursor = this.props.busyClass;
114-
}
115-
116-
return inlineStyles;
117-
}
118-
11920
render(): JSX.Element | null {
12021
return <button
12122
type="submit"
122-
disabled={this.Disabled}
23+
className={this.ClassName}
12324
style={this.InlineStyles}
25+
disabled={this.Disabled}
12426
>
12527
{this.props.children}
12628
</button>;

0 commit comments

Comments
 (0)