This is an automated email from the ASF dual-hosted git repository.
vogievetsky pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new b0c78ff295 Web console: make retention dialog clearer (#14793)
b0c78ff295 is described below
commit b0c78ff295533df80391109c9038a499271ab2a0
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Fri Aug 11 09:43:00 2023 -0700
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 <[email protected]>
* update snapshot for copy
---------
Co-authored-by: Suneet Saldanha <[email protected]>
---
.../src/components/json-input/json-input.tsx | 5 +-
.../src/components/rule-editor/rule-editor.tsx | 41 +--
.../__snapshots__/retention-dialog.spec.tsx.snap | 326 ++++++++++++++++++++-
.../dialogs/retention-dialog/retention-dialog.scss | 4 -
.../dialogs/retention-dialog/retention-dialog.tsx | 55 ++--
5 files changed, 363 insertions(+), 68 deletions(-)
diff --git a/web-console/src/components/json-input/json-input.tsx
b/web-console/src/components/json-input/json-input.tsx
index b206db77e9..f4ffddc111 100644
--- a/web-console/src/components/json-input/json-input.tsx
+++ b/web-console/src/components/json-input/json-input.tsx
@@ -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%'}
diff --git a/web-console/src/components/rule-editor/rule-editor.tsx
b/web-console/src/components/rule-editor/rule-editor.tsx
index 52eb5b805c..3f72a8ca41 100644
--- a/web-console/src/components/rule-editor/rule-editor.tsx
+++ b/web-console/src/components/rule-editor/rule-editor.tsx
@@ -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"
/>
)}
diff --git
a/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
b/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
index bc3c6b13c3..68fe45ae1b 100644
---
a/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
+++
b/web-console/src/dialogs/retention-dialog/__snapshots__/retention-dialog.spec.tsx.snap
@@ -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>
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.scss
b/web-console/src/dialogs/retention-dialog/retention-dialog.scss
index df521abbd1..fb55a8d445 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.scss
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.scss
@@ -45,9 +45,5 @@
padding: 0 15px;
}
}
-
- .default-rule {
- margin-top: 10px;
- }
}
}
diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
index 6420413706..677173cc6d 100644
--- a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
+++ b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx
@@ -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>
</>
)}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]