mirror of
https://github.com/apache/druid.git
synced 2025-02-28 06:19:13 +00:00
Web console: make retention dialog clearer (#14793)
* make retention dialog clearer * tweak * another tweak * Update web-console/src/dialogs/retention-dialog/retention-dialog.tsx Co-authored-by: Suneet Saldanha <suneet@apache.org> * update snapshot for copy --------- Co-authored-by: Suneet Saldanha <suneet@apache.org>
This commit is contained in:
parent
a0234c4e13
commit
b0c78ff295
@ -66,7 +66,7 @@ interface InternalValue {
|
||||
|
||||
interface JsonInputProps {
|
||||
value: any;
|
||||
onChange: (value: any) => void;
|
||||
onChange?: (value: any) => void;
|
||||
setError?: (error: Error | undefined) => void;
|
||||
placeholder?: string;
|
||||
focus?: boolean;
|
||||
@ -123,7 +123,7 @@ export const JsonInput = React.memo(function JsonInput(props: JsonInputProps) {
|
||||
|
||||
setError?.(error);
|
||||
if (!error) {
|
||||
onChange(value);
|
||||
onChange?.(value);
|
||||
}
|
||||
|
||||
if (showErrorIfNeeded) {
|
||||
@ -131,6 +131,7 @@ export const JsonInput = React.memo(function JsonInput(props: JsonInputProps) {
|
||||
}
|
||||
}}
|
||||
onBlur={() => setShowErrorIfNeeded(true)}
|
||||
readOnly={!onChange}
|
||||
focus={focus}
|
||||
fontSize={12}
|
||||
width={width || '100%'}
|
||||
|
@ -42,22 +42,23 @@ const PERIOD_SUGGESTIONS: string[] = ['P1D', 'P7D', 'P1M', 'P1Y', 'P1000Y'];
|
||||
export interface RuleEditorProps {
|
||||
rule: Rule;
|
||||
tiers: string[];
|
||||
onChange(newRule: Rule): void;
|
||||
onDelete(): void;
|
||||
moveUp: (() => void) | undefined;
|
||||
moveDown: (() => void) | undefined;
|
||||
onChange?: (newRule: Rule) => void;
|
||||
onDelete?: () => void;
|
||||
moveUp?: () => void;
|
||||
moveDown?: () => void;
|
||||
}
|
||||
|
||||
export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps) {
|
||||
const { rule, onChange, tiers, onDelete, moveUp, moveDown } = props;
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const disabled = !onChange;
|
||||
|
||||
function removeTier(key: string) {
|
||||
const newTierReplicants = { ...rule.tieredReplicants };
|
||||
delete newTierReplicants[key];
|
||||
|
||||
const newRule = { ...rule, tieredReplicants: newTierReplicants };
|
||||
onChange(newRule);
|
||||
onChange?.(newRule);
|
||||
}
|
||||
|
||||
function addTier() {
|
||||
@ -72,7 +73,7 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
||||
}
|
||||
}
|
||||
|
||||
onChange(RuleUtil.addTieredReplicant(rule, newTierName, 1));
|
||||
onChange?.(RuleUtil.addTieredReplicant(rule, newTierName, 1));
|
||||
}
|
||||
|
||||
function renderTiers() {
|
||||
@ -90,14 +91,15 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
||||
<FormGroup>
|
||||
{tieredReplicantsList.map(([tier, replication]) => (
|
||||
<ControlGroup key={tier}>
|
||||
<Button minimal style={{ pointerEvents: 'none' }}>
|
||||
<Button minimal disabled={disabled} style={{ pointerEvents: 'none' }}>
|
||||
Tier:
|
||||
</Button>
|
||||
<HTMLSelect
|
||||
fill
|
||||
value={tier}
|
||||
disabled={disabled}
|
||||
onChange={(e: any) =>
|
||||
onChange(RuleUtil.renameTieredReplicants(rule, tier, e.target.value))
|
||||
onChange?.(RuleUtil.renameTieredReplicants(rule, tier, e.target.value))
|
||||
}
|
||||
>
|
||||
<option key={tier} value={tier}>
|
||||
@ -111,19 +113,20 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
||||
</option>
|
||||
))}
|
||||
</HTMLSelect>
|
||||
<Button minimal style={{ pointerEvents: 'none' }}>
|
||||
<Button minimal disabled={disabled} style={{ pointerEvents: 'none' }}>
|
||||
Replicants:
|
||||
</Button>
|
||||
<NumericInput
|
||||
value={replication}
|
||||
disabled={disabled}
|
||||
onValueChange={(v: number) => {
|
||||
if (isNaN(v)) return;
|
||||
onChange(RuleUtil.addTieredReplicant(rule, tier, v));
|
||||
onChange?.(RuleUtil.addTieredReplicant(rule, tier, v));
|
||||
}}
|
||||
min={0}
|
||||
max={256}
|
||||
/>
|
||||
<Button onClick={() => removeTier(tier)} icon={IconNames.TRASH} />
|
||||
{onChange && <Button onClick={() => removeTier(tier)} icon={IconNames.TRASH} />}
|
||||
</ControlGroup>
|
||||
))}
|
||||
</FormGroup>
|
||||
@ -131,7 +134,7 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
||||
}
|
||||
|
||||
function renderTierAdder() {
|
||||
const { rule, tiers } = props;
|
||||
if (!onChange) return;
|
||||
const disabled = Object.keys(rule.tieredReplicants || {}).length >= Object.keys(tiers).length;
|
||||
|
||||
return (
|
||||
@ -163,7 +166,7 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
||||
<div className="spacer" />
|
||||
{moveUp && <Button minimal icon={IconNames.ARROW_UP} onClick={moveUp} />}
|
||||
{moveDown && <Button minimal icon={IconNames.ARROW_DOWN} onClick={moveDown} />}
|
||||
<Button minimal icon={IconNames.TRASH} onClick={onDelete} />
|
||||
{onDelete && <Button minimal icon={IconNames.TRASH} onClick={onDelete} />}
|
||||
</div>
|
||||
|
||||
<Collapse isOpen={isOpen}>
|
||||
@ -172,7 +175,8 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
||||
<ControlGroup>
|
||||
<HTMLSelect
|
||||
value={rule.type}
|
||||
onChange={(e: any) => onChange(RuleUtil.changeRuleType(rule, e.target.value))}
|
||||
disabled={disabled}
|
||||
onChange={(e: any) => onChange?.(RuleUtil.changeRuleType(rule, e.target.value))}
|
||||
>
|
||||
{RuleUtil.TYPES.map(type => (
|
||||
<option key={type} value={type}>
|
||||
@ -184,9 +188,10 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
||||
<SuggestibleInput
|
||||
value={rule.period || ''}
|
||||
sanitizer={durationSanitizer}
|
||||
disabled={disabled}
|
||||
onValueChange={period => {
|
||||
if (typeof period === 'undefined') return;
|
||||
onChange(RuleUtil.changePeriod(rule, period));
|
||||
onChange?.(RuleUtil.changePeriod(rule, period));
|
||||
}}
|
||||
placeholder={PERIOD_SUGGESTIONS[0]}
|
||||
suggestions={PERIOD_SUGGESTIONS}
|
||||
@ -197,15 +202,17 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
||||
className="include-future"
|
||||
checked={rule.includeFuture || false}
|
||||
label="Include future"
|
||||
disabled={disabled}
|
||||
onChange={() => {
|
||||
onChange(RuleUtil.changeIncludeFuture(rule, !rule.includeFuture));
|
||||
onChange?.(RuleUtil.changeIncludeFuture(rule, !rule.includeFuture));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{RuleUtil.hasInterval(rule) && (
|
||||
<InputGroup
|
||||
value={rule.interval || ''}
|
||||
onChange={(e: any) => onChange(RuleUtil.changeInterval(rule, e.target.value))}
|
||||
readOnly={!onChange}
|
||||
onChange={(e: any) => onChange?.(RuleUtil.changeInterval(rule, e.target.value))}
|
||||
placeholder="2010-01-01/2020-01-01"
|
||||
/>
|
||||
)}
|
||||
|
@ -580,31 +580,325 @@ exports[`RetentionDialog matches snapshot 1`] = `
|
||||
<div
|
||||
class="bp4-form-group"
|
||||
>
|
||||
<label
|
||||
class="bp4-label"
|
||||
>
|
||||
Cluster defaults (
|
||||
<a>
|
||||
edit
|
||||
</a>
|
||||
)
|
||||
|
||||
<span
|
||||
class="bp4-text-muted"
|
||||
/>
|
||||
</label>
|
||||
<div
|
||||
class="bp4-form-content"
|
||||
>
|
||||
<p>
|
||||
Cluster defaults (
|
||||
<a>
|
||||
edit
|
||||
</a>
|
||||
):
|
||||
The cluster default rules are evaluated if none of the above rules match.
|
||||
</p>
|
||||
<div
|
||||
class="default-rule"
|
||||
class="rule-editor"
|
||||
>
|
||||
<button
|
||||
class="bp4-button bp4-disabled"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
<div
|
||||
class="title"
|
||||
>
|
||||
<span
|
||||
class="bp4-button-text"
|
||||
<button
|
||||
class="bp4-button bp4-minimal left"
|
||||
type="button"
|
||||
>
|
||||
loadForever(2x)
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="bp4-button-text"
|
||||
>
|
||||
loadForever(2x)
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="bp4-icon bp4-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
role="img"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="spacer"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="bp4-collapse"
|
||||
style="height: auto; overflow-y: visible; transition: none;"
|
||||
>
|
||||
<div
|
||||
aria-hidden="false"
|
||||
class="bp4-collapse-body"
|
||||
style="transform: translateY(0); transition: none;"
|
||||
>
|
||||
<div
|
||||
class="bp4-card bp4-elevation-2"
|
||||
>
|
||||
<div
|
||||
class="bp4-form-group"
|
||||
>
|
||||
<div
|
||||
class="bp4-form-content"
|
||||
>
|
||||
<div
|
||||
class="bp4-control-group"
|
||||
>
|
||||
<div
|
||||
class="bp4-html-select bp4-disabled"
|
||||
>
|
||||
<select
|
||||
disabled=""
|
||||
>
|
||||
<option
|
||||
value="loadForever"
|
||||
>
|
||||
loadForever
|
||||
</option>
|
||||
<option
|
||||
value="loadByInterval"
|
||||
>
|
||||
loadByInterval
|
||||
</option>
|
||||
<option
|
||||
value="loadByPeriod"
|
||||
>
|
||||
loadByPeriod
|
||||
</option>
|
||||
<option
|
||||
value="dropForever"
|
||||
>
|
||||
dropForever
|
||||
</option>
|
||||
<option
|
||||
value="dropByInterval"
|
||||
>
|
||||
dropByInterval
|
||||
</option>
|
||||
<option
|
||||
value="dropByPeriod"
|
||||
>
|
||||
dropByPeriod
|
||||
</option>
|
||||
<option
|
||||
value="dropBeforeByPeriod"
|
||||
>
|
||||
dropBeforeByPeriod
|
||||
</option>
|
||||
<option
|
||||
value="broadcastForever"
|
||||
>
|
||||
broadcastForever
|
||||
</option>
|
||||
<option
|
||||
value="broadcastByInterval"
|
||||
>
|
||||
broadcastByInterval
|
||||
</option>
|
||||
<option
|
||||
value="broadcastByPeriod"
|
||||
>
|
||||
broadcastByPeriod
|
||||
</option>
|
||||
</select>
|
||||
<span
|
||||
class="bp4-icon bp4-icon-double-caret-vertical"
|
||||
icon="double-caret-vertical"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="iconTitle-12"
|
||||
data-icon="double-caret-vertical"
|
||||
height="16"
|
||||
role="img"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<title
|
||||
id="iconTitle-12"
|
||||
>
|
||||
Open dropdown
|
||||
</title>
|
||||
<path
|
||||
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bp4-form-group"
|
||||
>
|
||||
<div
|
||||
class="bp4-form-content"
|
||||
>
|
||||
<div
|
||||
class="bp4-control-group"
|
||||
>
|
||||
<button
|
||||
class="bp4-button bp4-disabled bp4-minimal"
|
||||
disabled=""
|
||||
style="pointer-events: none;"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp4-button-text"
|
||||
>
|
||||
Tier:
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="bp4-html-select bp4-disabled bp4-fill"
|
||||
>
|
||||
<select
|
||||
disabled=""
|
||||
>
|
||||
<option
|
||||
value="_default_tier"
|
||||
>
|
||||
_default_tier
|
||||
</option>
|
||||
</select>
|
||||
<span
|
||||
class="bp4-icon bp4-icon-double-caret-vertical"
|
||||
icon="double-caret-vertical"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="iconTitle-13"
|
||||
data-icon="double-caret-vertical"
|
||||
height="16"
|
||||
role="img"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<title
|
||||
id="iconTitle-13"
|
||||
>
|
||||
Open dropdown
|
||||
</title>
|
||||
<path
|
||||
d="M5 7h6a1.003 1.003 0 00.71-1.71l-3-3C8.53 2.11 8.28 2 8 2s-.53.11-.71.29l-3 3A1.003 1.003 0 005 7zm6 2H5a1.003 1.003 0 00-.71 1.71l3 3c.18.18.43.29.71.29s.53-.11.71-.29l3-3A1.003 1.003 0 0011 9z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
class="bp4-button bp4-disabled bp4-minimal"
|
||||
disabled=""
|
||||
style="pointer-events: none;"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp4-button-text"
|
||||
>
|
||||
Replicants:
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="bp4-control-group bp4-numeric-input"
|
||||
>
|
||||
<div
|
||||
class="bp4-input-group bp4-disabled"
|
||||
>
|
||||
<input
|
||||
aria-valuemax="256"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="2"
|
||||
autocomplete="off"
|
||||
class="bp4-input"
|
||||
disabled=""
|
||||
id="numericInput-1"
|
||||
max="256"
|
||||
min="0"
|
||||
role="spinbutton"
|
||||
type="text"
|
||||
value="2"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="bp4-button-group bp4-vertical bp4-fixed"
|
||||
>
|
||||
<button
|
||||
aria-controls="numericInput-1"
|
||||
aria-label="increment"
|
||||
class="bp4-button bp4-disabled"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="bp4-icon bp4-icon-chevron-up"
|
||||
icon="chevron-up"
|
||||
>
|
||||
<svg
|
||||
data-icon="chevron-up"
|
||||
height="16"
|
||||
role="img"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<path
|
||||
d="M12.71 9.29l-4-4C8.53 5.11 8.28 5 8 5s-.53.11-.71.29l-4 4a1.003 1.003 0 001.42 1.42L8 7.41l3.29 3.29c.18.19.43.3.71.3a1.003 1.003 0 00.71-1.71z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-controls="numericInput-1"
|
||||
aria-label="decrement"
|
||||
class="bp4-button bp4-disabled"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="bp4-icon bp4-icon-chevron-down"
|
||||
icon="chevron-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="chevron-down"
|
||||
height="16"
|
||||
role="img"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<path
|
||||
d="M12 5c-.28 0-.53.11-.71.29L8 8.59l-3.29-3.3a1.003 1.003 0 00-1.42 1.42l4 4c.18.18.43.29.71.29s.53-.11.71-.29l4-4A1.003 1.003 0 0012 5z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -45,9 +45,5 @@
|
||||
padding: 0 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.default-rule {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import { getLink } from '../../links';
|
||||
import { Api } from '../../singletons';
|
||||
import { filterMap, queryDruidSql, swapElements } from '../../utils';
|
||||
import type { Rule } from '../../utils/load-rule';
|
||||
import { RuleUtil } from '../../utils/load-rule';
|
||||
import { SnitchDialog } from '..';
|
||||
|
||||
import './retention-dialog.scss';
|
||||
@ -115,28 +114,6 @@ ORDER BY 1`,
|
||||
setCurrentRules(swapElements(currentRules, index, index + direction));
|
||||
}
|
||||
|
||||
function renderRule(rule: Rule, index: number) {
|
||||
return (
|
||||
<RuleEditor
|
||||
rule={rule}
|
||||
tiers={tiers}
|
||||
key={index}
|
||||
onChange={r => changeRule(r, index)}
|
||||
onDelete={() => deleteRule(index)}
|
||||
moveUp={index > 0 ? () => moveRule(index, -1) : undefined}
|
||||
moveDown={index < currentRules.length - 1 ? () => moveRule(index, 1) : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderDefaultRule(rule: Rule, index: number) {
|
||||
return (
|
||||
<div className="default-rule" key={index}>
|
||||
<Button disabled>{RuleUtil.ruleToString(rule)}</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SnitchDialog
|
||||
className="retention-dialog"
|
||||
@ -167,7 +144,17 @@ ORDER BY 1`,
|
||||
{currentTab === 'form' ? (
|
||||
<FormGroup>
|
||||
{currentRules.length ? (
|
||||
currentRules.map(renderRule)
|
||||
currentRules.map((rule, index) => (
|
||||
<RuleEditor
|
||||
key={index}
|
||||
rule={rule}
|
||||
tiers={tiers}
|
||||
onChange={r => changeRule(r, index)}
|
||||
onDelete={() => deleteRule(index)}
|
||||
moveUp={index > 0 ? () => moveRule(index, -1) : undefined}
|
||||
moveDown={index < currentRules.length - 1 ? () => moveRule(index, 1) : undefined}
|
||||
/>
|
||||
))
|
||||
) : datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE ? (
|
||||
<p className="no-rules-message">
|
||||
This datasource currently has no rules, it will use the cluster defaults.
|
||||
@ -194,11 +181,21 @@ ORDER BY 1`,
|
||||
{datasource !== CLUSTER_DEFAULT_FAKE_DATASOURCE && (
|
||||
<>
|
||||
<Divider />
|
||||
<FormGroup>
|
||||
<p>
|
||||
Cluster defaults (<a onClick={onEditDefaults}>edit</a>):
|
||||
</p>
|
||||
{defaultRules.map(renderDefaultRule)}
|
||||
<FormGroup
|
||||
label={
|
||||
<>
|
||||
Cluster defaults (<a onClick={onEditDefaults}>edit</a>)
|
||||
</>
|
||||
}
|
||||
>
|
||||
<p>The cluster default rules are evaluated if none of the above rules match.</p>
|
||||
{currentTab === 'form' ? (
|
||||
defaultRules.map((rule, index) => (
|
||||
<RuleEditor key={index} rule={rule} tiers={tiers} />
|
||||
))
|
||||
) : (
|
||||
<JsonInput value={defaultRules} />
|
||||
)}
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
|
Loading…
x
Reference in New Issue
Block a user