Web console: don't crash if cookies are totally disabled (#13013)

* fix local storage detection

* fix numeric input dialog
This commit is contained in:
Vadim Ogievetsky 2022-09-01 16:10:23 -07:00 committed by GitHub
parent 85d2a6d879
commit 0ae515bd3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 19 deletions

View File

@ -27,7 +27,7 @@ export async function createBrowser(): Promise<playwright.Browser> {
const headless = process.env['DRUID_E2E_TEST_HEADLESS'] || TRUE; const headless = process.env['DRUID_E2E_TEST_HEADLESS'] || TRUE;
const debug = headless !== TRUE; const debug = headless !== TRUE;
const launchOptions: any = { const launchOptions: any = {
args: [`--window-size=${WIDTH},${HEIGHT + PADDING}`], args: [`--window-size=${WIDTH},${HEIGHT + PADDING}`, `--disable-local-storage`],
}; };
if (debug) { if (debug) {
launchOptions.headless = false; launchOptions.headless = false;

View File

@ -26,6 +26,7 @@ interface NumericInputDialogProps {
message?: JSX.Element; message?: JSX.Element;
minValue?: number; minValue?: number;
initValue: number; initValue: number;
integer?: boolean;
onSubmit(value: number): void; onSubmit(value: number): void;
onClose(): void; onClose(): void;
} }
@ -33,9 +34,19 @@ interface NumericInputDialogProps {
export const NumericInputDialog = React.memo(function NumericInputDialog( export const NumericInputDialog = React.memo(function NumericInputDialog(
props: NumericInputDialogProps, props: NumericInputDialogProps,
) { ) {
const { title, message, minValue, initValue, onSubmit, onClose } = props; const { title, message, minValue, initValue, integer, onSubmit, onClose } = props;
const effectiveMinValue = minValue ?? DEFAULT_MIN_VALUE;
const [value, setValue] = useState<number>(initValue); const [valueString, setValueString] = useState<string>(String(initValue));
function done() {
let value = Math.max(Number(valueString) || 0, effectiveMinValue);
if (integer) {
value = Math.round(value);
}
onSubmit(value);
onClose();
}
return ( return (
<Dialog <Dialog
@ -48,30 +59,41 @@ export const NumericInputDialog = React.memo(function NumericInputDialog(
<div className={Classes.DIALOG_BODY}> <div className={Classes.DIALOG_BODY}>
{message} {message}
<NumericInput <NumericInput
value={value} value={valueString}
onValueChange={(v: number) => { onValueChange={(_, v) => {
if (isNaN(v)) return; // Constrain to only simple numeric characters
setValue(Math.max(v, DEFAULT_MIN_VALUE)); v = v.replace(/[^\d\-.]/, '');
if (integer) {
// If in integer mode throw away the decimal point
v = v.replace(/\./, '');
}
if (effectiveMinValue >= 0) {
// If in non-negative mode throw away the minus
v = v.replace(/-/, '');
}
setValueString(v);
}} }}
min={minValue ?? DEFAULT_MIN_VALUE} onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key !== 'Enter') return;
done();
}}
min={effectiveMinValue}
stepSize={1} stepSize={1}
minorStepSize={null} minorStepSize={null}
majorStepSize={10} majorStepSize={10}
fill fill
autoFocus autoFocus
selectAllOnFocus
allowNumericCharactersOnly
/> />
</div> </div>
<div className={Classes.DIALOG_FOOTER}> <div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}> <div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button text="Close" onClick={onClose} /> <Button text="Close" onClick={onClose} />
<Button <Button text="OK" intent={Intent.PRIMARY} onClick={done} />
text="OK"
intent={Intent.PRIMARY}
onClick={() => {
onSubmit(value);
onClose();
}}
/>
</div> </div>
</div> </div>
</Dialog> </Dialog>

View File

@ -18,6 +18,14 @@
import * as JSONBig from 'json-bigint-native'; import * as JSONBig from 'json-bigint-native';
function noLocalStorage(): boolean {
try {
return typeof localStorage === 'undefined';
} catch {
return true;
}
}
export const LocalStorageKeys = { export const LocalStorageKeys = {
CAPABILITIES_OVERRIDE: 'capabilities-override' as const, CAPABILITIES_OVERRIDE: 'capabilities-override' as const,
INGESTION_SPEC: 'ingestion-spec' as const, INGESTION_SPEC: 'ingestion-spec' as const,
@ -65,7 +73,7 @@ function prependNamespace(key: string): string {
} }
export function localStorageSet(key: LocalStorageKeys, value: string): void { export function localStorageSet(key: LocalStorageKeys, value: string): void {
if (typeof localStorage === 'undefined') return; if (noLocalStorage()) return;
try { try {
localStorage.setItem(prependNamespace(key), value); localStorage.setItem(prependNamespace(key), value);
} catch (e) { } catch (e) {
@ -78,7 +86,7 @@ export function localStorageSetJson(key: LocalStorageKeys, value: any): void {
} }
export function localStorageGet(key: LocalStorageKeys): string | undefined { export function localStorageGet(key: LocalStorageKeys): string | undefined {
if (typeof localStorage === 'undefined') return; if (noLocalStorage()) return;
try { try {
return localStorage.getItem(prependNamespace(key)) || localStorage.getItem(key) || undefined; return localStorage.getItem(prependNamespace(key)) || localStorage.getItem(key) || undefined;
} catch (e) { } catch (e) {
@ -98,7 +106,7 @@ export function localStorageGetJson(key: LocalStorageKeys): any {
} }
export function localStorageRemove(key: LocalStorageKeys): void { export function localStorageRemove(key: LocalStorageKeys): void {
if (typeof localStorage === 'undefined') return; if (noLocalStorage()) return;
try { try {
localStorage.removeItem(prependNamespace(key)); localStorage.removeItem(prependNamespace(key));
} catch (e) { } catch (e) {

View File

@ -103,6 +103,7 @@ export const MaxTasksButton = function MaxTasksButton(props: MaxTasksButtonProps
</> </>
} }
minValue={2} minValue={2}
integer
initValue={maxNumTasks} initValue={maxNumTasks}
onSubmit={p => { onSubmit={p => {
changeQueryContext(changeMaxNumTasks(queryContext, p)); changeQueryContext(changeMaxNumTasks(queryContext, p));