diff --git a/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap b/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap index 7246fe89490f..f5cc624dd213 100644 --- a/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap +++ b/web-console/src/components/rule-editor/__snapshots__/rule-editor.spec.tsx.snap @@ -169,7 +169,7 @@ exports[`RuleEditor matches snapshot no tier in rule 1`] = ` class="bp5-form-content" >
- - - + - - Open dropdown - - - - + + + + +
- - - + - - Open dropdown - - - - + + + + +
- - - + - - Open dropdown - - - - + + + + +
- - - + - - Open dropdown - - - - + + + + +
- - - + - - Open dropdown - - - - + + + + +
+
+
+ + + + + + +
+
+ +
+
+ +
+
+ + +
+
+ + +`; + +exports[`TieredReplicant matches snapshot with existing tier 1`] = ` +
+ +
+
+ + + + + + +
+
+ +
+
+ +
+
+ + +
+
+ +
+`; + +exports[`TieredReplicant matches snapshot with multiple used tiers 1`] = ` +
+ +
+
+ + + + + + +
+
+ +
+
+ +
+
+ + +
+
+ +
+`; + +exports[`TieredReplicant matches snapshot with non-existing tier 1`] = ` +
+ +
+
+ + + + + + +
+
+ +
+
+ +
+
+ + +
+
+ +
+`; + +exports[`TieredReplicant matches snapshot without remove button 1`] = ` +
+ +
+
+ + + + + + +
+
+ +
+
+ +
+
+ + +
+
+
+`; diff --git a/web-console/src/components/rule-editor/rule-editor.tsx b/web-console/src/components/rule-editor/rule-editor.tsx index 8cea5321cfd7..95da0d64c8c4 100644 --- a/web-console/src/components/rule-editor/rule-editor.tsx +++ b/web-console/src/components/rule-editor/rule-editor.tsx @@ -24,7 +24,6 @@ import { FormGroup, HTMLSelect, InputGroup, - NumericInput, Switch, } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; @@ -35,6 +34,8 @@ import { RuleUtil } from '../../druid-models'; import { durationSanitizer } from '../../utils'; import { SuggestibleInput } from '../suggestible-input/suggestible-input'; +import { TieredReplicant } from './tiered-replicant'; + import './rule-editor.scss'; const PERIOD_SUGGESTIONS: string[] = ['P1D', 'P7D', 'P1M', 'P1Y', 'P1000Y']; @@ -62,10 +63,22 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps) } function addTier() { - let newTierName = tiers[0]; + if (!rule.tieredReplicants) return; + + let newTierName: string | undefined; + + // Pick an existing tier that is not assigned + for (const tier of tiers) { + if (rule.tieredReplicants[tier] === undefined) { + newTierName = tier; + break; + } + } - if (rule.tieredReplicants) { - for (const tier of tiers) { + // If no such tier exists, pick a new tier name + if (!newTierName) { + for (let i = 1; i < 100; i++) { + const tier = `tier${i}`; if (rule.tieredReplicants[tier] === undefined) { newTierName = tier; break; @@ -73,7 +86,9 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps) } } - onChange?.(RuleUtil.addTieredReplicant(rule, newTierName, 1)); + if (newTierName) { + onChange?.(RuleUtil.addTieredReplicant(rule, newTierName, 1)); + } } function renderTiers() { @@ -89,45 +104,22 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps) return ( - {tieredReplicantsList.map(([tier, replication]) => ( - - - - onChange?.(RuleUtil.renameTieredReplicants(rule, tier, e.target.value)) - } - > - - {tiers - .filter(t => t !== tier && !tieredReplicants[t]) - .map(t => ( - - ))} - - - { - if (isNaN(v)) return; - onChange?.(RuleUtil.addTieredReplicant(rule, tier, v)); - }} - min={0} - max={256} - /> - {onChange && diff --git a/web-console/src/components/rule-editor/tiered-replicant.spec.tsx b/web-console/src/components/rule-editor/tiered-replicant.spec.tsx new file mode 100644 index 000000000000..d821b56336a1 --- /dev/null +++ b/web-console/src/components/rule-editor/tiered-replicant.spec.tsx @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { render } from '@testing-library/react'; + +import { TieredReplicant } from './tiered-replicant'; + +describe('TieredReplicant', () => { + it('matches snapshot with existing tier', () => { + const tieredReplicant = ( + {}} + onChangeReplication={() => {}} + onRemove={() => {}} + /> + ); + const { container } = render(tieredReplicant); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('matches snapshot with non-existing tier', () => { + const tieredReplicant = ( + {}} + onChangeReplication={() => {}} + onRemove={() => {}} + /> + ); + const { container } = render(tieredReplicant); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('matches snapshot when disabled', () => { + const tieredReplicant = ( + {}} + onChangeReplication={() => {}} + onRemove={() => {}} + /> + ); + const { container } = render(tieredReplicant); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('matches snapshot without remove button', () => { + const tieredReplicant = ( + {}} + onChangeReplication={() => {}} + onRemove={undefined} + /> + ); + const { container } = render(tieredReplicant); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('matches snapshot with multiple used tiers', () => { + const tieredReplicant = ( + {}} + onChangeReplication={() => {}} + onRemove={() => {}} + /> + ); + const { container } = render(tieredReplicant); + expect(container.firstChild).toMatchSnapshot(); + }); +}); diff --git a/web-console/src/components/rule-editor/tiered-replicant.tsx b/web-console/src/components/rule-editor/tiered-replicant.tsx new file mode 100644 index 000000000000..43d8b39bb154 --- /dev/null +++ b/web-console/src/components/rule-editor/tiered-replicant.tsx @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Button, ControlGroup, NumericInput } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; +import React from 'react'; + +import { SuggestibleInput } from '../suggestible-input/suggestible-input'; + +export interface TieredReplicantProps { + tier: string; + replication: number; + onChangeTier: (newTier: string) => void; + onChangeReplication: (value: number) => void; + tiers: string[]; + usedTiers: string[]; + disabled: boolean; + onRemove?: () => void; +} + +export const TieredReplicant = React.memo(function TieredReplicant(props: TieredReplicantProps) { + const { + tier, + replication, + tiers, + usedTiers, + disabled, + onChangeReplication, + onChangeTier, + onRemove, + } = props; + + return ( + + + onChangeTier(value || '')} + suggestions={tiers.filter(t => t === tier || !usedTiers.includes(t))} + /> + + { + if (isNaN(v)) return; + onChangeReplication(v); + }} + min={0} + max={256} + /> + {onRemove &&
- - - + - - Open dropdown - - - - + + + + +
- - - - - Open dropdown - - - - + + + + +