mirror of https://github.com/apache/druid.git
Web console: don't crash if cookies are totally disabled (#13013)
* fix local storage detection * fix numeric input dialog
This commit is contained in:
parent
85d2a6d879
commit
0ae515bd3c
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Loading…
Reference in New Issue