Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const RuleFormBatchEdit = ({
onClose,
onUpdate,
}: {
ruleIds: number[];
ruleIds: Set<number>;
onClose: () => void;
onUpdate: () => void;
}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { errorToString } from '../../utils/error';
import { DraftRule, RuleData } from './useRule';
import { ErrorIResponse, responseHandler } from '../../utils/api';

export function useBatchRules(ruleIds: number[] | undefined) {
export function useBatchRules(ruleIds: Set<number> | undefined) {
const [isLoading, setIsLoading] = useState(false);
const [errors, setErrors] = useState<string | undefined>(undefined);
const [rules, setRules] = useState<RuleData[] | undefined>(undefined);
Expand Down Expand Up @@ -97,7 +97,7 @@ export function useBatchRules(ruleIds: number[] | undefined) {

useEffect(() => {
if (ruleIds) {
fetchRules(ruleIds);
fetchRules([...ruleIds]);
} else {
setRules([]);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { isEqual, identity } from 'lodash';

export const useStringParam = (paramKey: string) => {
const [searchParams, setSearchParams] = useSearchParams();

const paramValue = useMemo(
() => searchParams.get(paramKey) || '',
[searchParams],
);

const setParamValue = useMemo(
() => (value: string) => {
searchParams.set(paramKey, value);
setSearchParams(searchParams);
},
[searchParams],
);

return [paramValue, setParamValue] as const;
};

const getArrayValuesFromSearchParams = <T>(
searchParams: URLSearchParams,
paramKey: string,
transformer: (paramVal: string) => T,
) => {
const incomingValue: T[] = [];
searchParams.forEach((val, key) => {
if (key === paramKey) {
incomingValue.push(transformer(val));
}
});
return incomingValue;
};

const getSetValuesFromSearchParams = <T>(
searchParams: URLSearchParams,
paramKey: string,
transformer: (paramVal: string) => T,
) => {
const incomingValue = new Set<T>();
searchParams.forEach((val, key) => {
if (key === paramKey) {
incomingValue.add(transformer(val));
}
});
return incomingValue;
};

const useCollectionParam = <I extends Set<T> | Array<T>, T = string>(
paramKey: string,
transformIn: (paramVal: string) => T = identity,
transformOut: (paramVal: T) => string = identity,
getValuesFromSearchParams: (
searchParams: URLSearchParams,
paramKey: string,
transformer: (paramVal: string) => T,
) => I,
) => {
const [searchParams, setSearchParams] = useSearchParams();
const [localParamValue, setLocalParamValue] = useState<I>(() =>
getValuesFromSearchParams(searchParams, paramKey, transformIn),
);

useEffect(() => {
const incomingValue = getValuesFromSearchParams(
searchParams,
paramKey,
transformIn,
);

/**
* We take care to preserve object identities here, as a change to the
* query string should not change the identity of the tag or ruleType
* options arrays.
*/
if (!isEqual(incomingValue, localParamValue)) {
setLocalParamValue(incomingValue);
}
}, [searchParams]);

const setParamValue = useMemo(
() => (newValue: I) => {
searchParams.delete(paramKey);
for (const value of newValue) {
searchParams.append(paramKey, transformOut(value));
}
setSearchParams(searchParams);
},
[searchParams],
);

return [localParamValue, setParamValue] as const;
};

export const useArrayParam = <T = string>(
paramKey: string,
transformIn: (paramVal: string) => T = identity,
transformOut: (paramVal: T) => string = identity,
) =>
useCollectionParam(
paramKey,
transformIn,
transformOut,
getArrayValuesFromSearchParams,
);

export const useSetParam = <T = string>(
paramKey: string,
transformIn: (paramVal: string) => T = identity,
transformOut: (paramVal: T) => string = identity,
) =>
useCollectionParam(
paramKey,
transformIn,
transformOut,
getSetValuesFromSearchParams,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useReducer } from 'react';
import { PaginatedRuleData } from './useRules';

export type RowState = Set<number>;
export type RowAction =
| { type: 'add'; id: number }
| { type: 'delete'; id: number }
| { type: 'set'; ids: number[] }
| { type: 'clear' }
| { type: 'selectAll' };

export const useRuleSelection = (
initialRules: Set<number>,
ruleData: PaginatedRuleData | null,
) =>
useReducer((selectedRows: RowState, action: RowAction): RowState => {
switch (action.type) {
case 'set': {
return new Set(action.ids);
}
case 'add': {
const nextRowSelection = new Set(selectedRows);
nextRowSelection.add(action.id);
return nextRowSelection;
}
case 'delete': {
const nextRowSelection = new Set(selectedRows);
nextRowSelection.delete(action.id);
return nextRowSelection;
}
case 'clear': {
return new Set();
}
case 'selectAll': {
return !ruleData?.data || selectedRows.size === ruleData.data.length
? new Set()
: new Set(ruleData.data.map((rule) => rule.id as number));
}
}
}, initialRules);
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type SortColumns = Array<{
export function useRules() {
const { location } = window;
const [ruleData, setRulesData] = useState<PaginatedRuleData | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [isLoading, setIsLoading] = useState(true); // Assume that if we are using rules, we will be fetching immediately
const [error, setError] = useState<string | undefined>(undefined);
const [isRefreshing, setIsRefreshing] = useState(false);
const [abortController, setAbortController] = useState<AbortController>();
Expand Down
4 changes: 4 additions & 0 deletions apps/rule-manager/client/src/ts/components/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import { icon as sortRight } from '@elastic/eui/src/components/icon/assets/sortR
import { icon as fullScreenExit } from '@elastic/eui/src/components/icon/assets/fullScreenExit';
import { icon as tokenString } from '@elastic/eui/src/components/icon/assets/tokenString';
import { icon as beaker } from '@elastic/eui/src/components/icon/assets/beaker';
import { icon as menuLeft } from '@elastic/eui/src/components/icon/assets/menuLeft';
import { icon as menuRight } from '@elastic/eui/src/components/icon/assets/menuRight';

const cachedIcons = {
apps,
Expand Down Expand Up @@ -81,6 +83,8 @@ const cachedIcons = {
expandMini,
tokenString,
beaker,
menuLeft,
menuRight,
};

appendIconComponentCache(cachedIcons);
Loading
Loading