mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
d92a761f6e
Summary: - Showing nudge to individual users to sign up for free team plan. - Implementing billing page to upgrade from free team to pro. - New modal with upgrade options and free team site signup. - Integrating Stripe-hosted UI for checkout and plan management. Test Plan: updated tests Reviewers: georgegevoian Reviewed By: georgegevoian Subscribers: paulfitz Differential Revision: https://phab.getgrist.com/D3456
122 lines
3.8 KiB
TypeScript
122 lines
3.8 KiB
TypeScript
import { Disposable, dom, Observable, styled } from 'grainjs';
|
|
import { colors } from 'app/client/ui2018/cssVars';
|
|
|
|
/**
|
|
* Simple validation controls. Renders as a red text with a validation message.
|
|
*
|
|
* Sample usage:
|
|
*
|
|
* const group = new ValidationGroup();
|
|
* async function save() {
|
|
* if (await group.validate()) {
|
|
* api.save(....)
|
|
* }
|
|
* }
|
|
* ....
|
|
* dom('div',
|
|
* dom('Login', 'Enter login', input(login), group.resetInput()),
|
|
* dom.create(Validator, accountGroup, 'Login is required', () => Boolean(login.get()) === true)),
|
|
* dom.create(Validator, accountGroup, 'Login must by unique', async () => await throwsIfLoginIsTaken(login.get())),
|
|
* dom('button', dom.on('click', save))
|
|
* )
|
|
*/
|
|
|
|
/**
|
|
* Validation function. Can return either boolean value or throw an error with a message that will be displayed
|
|
* in a validator instance.
|
|
*/
|
|
type ValidationFunction = () => (boolean | Promise<boolean> | void | Promise<void>)
|
|
|
|
/**
|
|
* Validation groups allow you to organize validator controls on a page as a set.
|
|
* Each validation group can perform validation independently from other validation groups on the page.
|
|
*/
|
|
export class ValidationGroup {
|
|
// List of attached validators.
|
|
private _validators: Validator[] = [];
|
|
/**
|
|
* Runs all validators check functions. Returns result of the validation.
|
|
*/
|
|
public async validate() {
|
|
let valid = true;
|
|
for (const val of this._validators) {
|
|
try {
|
|
const result = await val.check();
|
|
// Validator can either return boolean, Promise<boolean> or void. Booleans are straightforwards.
|
|
// When validator has a void/Promise<void> result it means that it just asserts certain invariant, and should
|
|
// throw an exception when this invariant is not met. Error message can be used to amend the message in the
|
|
// validator instance.
|
|
const isValid = typeof result === 'boolean' ? result : true;
|
|
val.set(isValid);
|
|
if (!isValid) { valid = false; break; }
|
|
} catch (err) {
|
|
valid = false;
|
|
val.set((err as Error).message);
|
|
break;
|
|
}
|
|
}
|
|
return valid;
|
|
}
|
|
/**
|
|
* Attaches single validator instance to this group. Validator can be in multiple groups
|
|
* at the same time.
|
|
*/
|
|
public add(validator: Validator) {
|
|
this._validators.push(validator);
|
|
}
|
|
/**
|
|
* Helper that can be attached to the input element to reset validation status.
|
|
*/
|
|
public inputReset() {
|
|
return dom.on('input', this.reset.bind(this));
|
|
}
|
|
/**
|
|
* Reset all validators statuses.
|
|
*/
|
|
public reset() {
|
|
this._validators.forEach(val => val.set(true));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validator instance. When triggered shows a red text with an error message.
|
|
*/
|
|
export class Validator extends Disposable {
|
|
private _isValid = Observable.create(this, true);
|
|
private _message = Observable.create(this, '');
|
|
constructor(public group: ValidationGroup, message: string, public check: ValidationFunction) {
|
|
super();
|
|
group.add(this);
|
|
this._message.set(message);
|
|
}
|
|
/**
|
|
* Helper that can be attached to the input element to reset validation status.
|
|
*/
|
|
public inputReset() {
|
|
return dom.on('input', this.set.bind(this, true));
|
|
}
|
|
/**
|
|
* Sets the validation status. If isValid is a string it is treated as a falsy value, and will
|
|
* mark this validator as invalid.
|
|
*/
|
|
public set(isValid: boolean | string) {
|
|
if (this.isDisposed()) { return; }
|
|
if (typeof isValid === 'string') {
|
|
this._message.set(isValid);
|
|
this._isValid.set(!isValid);
|
|
} else {
|
|
this._isValid.set(isValid ? true : false);
|
|
}
|
|
}
|
|
public buildDom() {
|
|
return cssError(
|
|
dom.text(this._message),
|
|
dom.hide(this._isValid),
|
|
);
|
|
}
|
|
}
|
|
|
|
const cssError = styled('div.validator', `
|
|
color: ${colors.error};
|
|
`);
|