Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: create and organise CheckList page folder with dependent components #998

Merged
merged 17 commits into from
Nov 29, 2024
Merged
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
21 changes: 21 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,24 @@ Grafana configuration can be adjusted using the `custom.ini` file located in `/d
Grafana Enterprise adds features which the plugin takes advantage of (e.g. RBAC). To run the development environment with Grafana Enterprise features enabled you need to add a valid Grafana Enterprise license by updating `dev/license.jwt`. It has been added to our `.gitignore` file to ensure your license doesn't get added to any pull requests (we wouldn't want that happening again...).

When running `yarn server` if `dev/license.jwt` doesn't exist it will create it for you with no content present. You are free to update this file with your own license.

### IDE setup

We make no distinction of what IDE you should be using, however we do have some recommendations that if your IDE allows, you should enable:

#### Format on save

We use both [prettier](https://prettier.io/) and [eslint](https://eslint.org/) with the [@grafana/eslint-config package](https://www.npmjs.com/package/@grafana/eslint-config) which [we have chosen to extend](./.eslintrc.js). Our CI/CD pipelines expect these rules to be adherred to and will fail any submitted PRs if not. We recommend you enable format on save to ensure you are always adhering to these rules with as little friction as possible.

#### File nesting

We use the file nesting feature to help manage the growing number of files in the project. We recommend you enable this feature in your IDE to help keep your project organized and add the following rule (example is for VSCode):

```json
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.tsx": "${capture}.constants.ts, ${capture}.hooks.ts, ${capture}.test.tsx, ${capture}.types.ts, ${capture}.utils.ts"
}
```


11 changes: 0 additions & 11 deletions src/__mocks__/MockFormWrapper.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions src/__mocks__/raw-loader.js

This file was deleted.

1 change: 1 addition & 0 deletions src/components/AddNewCheckButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { AddNewCheckButton } from './AddNewCheckButton';
1 change: 0 additions & 1 deletion src/components/AlertSensitivityBadge/index.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/components/AlertStatus/index.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/components/BulkEditModal/index.ts

This file was deleted.

22 changes: 0 additions & 22 deletions src/components/CheckList/CheckListViewSwitcher.tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/components/CheckListItem/index.ts

This file was deleted.

49 changes: 0 additions & 49 deletions src/components/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import {
AlertSeverity,
BrowserCheck,
Check,
CheckEnabledStatus,
CheckListViewType,
CheckSort,
CheckType,
DNSCheck,
DnsProtocol,
Expand Down Expand Up @@ -443,52 +440,6 @@ export const getDefaultAlertAnnotations = (percentage: number) => ({
summary: `check success below ${percentage}%`,
});

export const CHECK_LIST_SORT_OPTIONS = [
{
label: 'A-Z',
value: CheckSort.AToZ,
},
{
label: 'Z-A',
value: CheckSort.ZToA,
},
{
label: 'Asc. Reachability ',
value: CheckSort.ReachabilityAsc,
},
{
label: 'Desc. Reachability ',
value: CheckSort.ReachabilityDesc,
},
{
label: 'Asc. Executions ',
value: CheckSort.ExecutionsAsc,
},
{
label: 'Desc. Executions ',
value: CheckSort.ExecutionsDesc,
},
];

export const CHECK_LIST_STATUS_OPTIONS: Array<SelectableValue<CheckEnabledStatus>> = [
{ label: 'All', value: CheckEnabledStatus.All },
{ label: 'Enabled', value: CheckEnabledStatus.Enabled },
{ label: 'Disabled', value: CheckEnabledStatus.Disabled },
];

export const CHECK_LIST_VIEW_TYPE_OPTIONS = [
{ description: 'Card view', value: CheckListViewType.Card, icon: 'check-square' },
{ description: 'List view', value: CheckListViewType.List, icon: 'list-ul' },
{ description: 'Visualization view', value: CheckListViewType.Viz, icon: 'gf-grid' },
];

export const CHECKS_PER_PAGE_CARD = 15;
export const CHECKS_PER_PAGE_LIST = 50;

export const CHECK_LIST_VIEW_TYPE_LS_KEY = 'grafana.sm.checklist.viewType';

export const CHECK_LIST_ICON_OVERLAY_LS_KEY = 'grafana.sm.checklist.iconOverlay';

export const HTTP_COMPRESSION_ALGO_OPTIONS = [
{ label: 'none', value: HTTPCompressionAlgo.none },
{ label: 'identity', value: HTTPCompressionAlgo.identity },
Expand Down
9 changes: 9 additions & 0 deletions src/page/CheckList/CheckList.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SelectableValue } from '@grafana/data';

import { CheckEnabledStatus } from 'types';

export const CHECK_LIST_STATUS_OPTIONS: Array<SelectableValue<CheckEnabledStatus>> = [
{ label: 'All', value: CheckEnabledStatus.All },
{ label: 'Enabled', value: CheckEnabledStatus.Enabled },
{ label: 'Disabled', value: CheckEnabledStatus.Disabled },
];
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { SelectableValue } from '@grafana/data';
import { capitalize } from 'lodash';

import { CheckEnabledStatus, CheckType, CheckTypeFilter, ProbeFilter } from 'types';
import { CheckTypeFilter, ProbeFilter } from 'page/CheckList/CheckList.types';
import { CheckEnabledStatus, CheckType } from 'types';
import { useProbes } from 'data/useProbes';
import { defaultFilters } from 'components/CheckFilters';

import { useQueryParametersState } from './useQueryParametersState';

export type FilterType = 'search' | 'labels' | 'type' | 'status' | 'probes';
import { useQueryParametersState } from 'hooks/useQueryParametersState';
import { defaultFilters } from 'page/CheckList/CheckList.utils';

interface CheckFiltersProps {
search: [state: string, update: (value: string | null) => void];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ import { Pagination, useStyles2 } from '@grafana/ui';
import { css } from '@emotion/css';
import { getTotalChecksPerMonth } from 'checkUsageCalc';

import { Check, CheckEnabledStatus, CheckFiltersType, CheckListViewType, CheckSort, CheckType, Label } from 'types';
import { CheckFiltersType, CheckListViewType, FilterType } from 'page/CheckList/CheckList.types';
import { Check, CheckEnabledStatus, CheckSort, CheckType, Label } from 'types';
import { MetricCheckSuccess, Time } from 'datasource/responses.types';
import { useSuspenseChecks } from 'data/useChecks';
import { useChecksReachabilitySuccessRate } from 'data/useSuccessRates';
import { findCheckinMetrics } from 'data/utils';
import { FilterType, useCheckFilters } from 'hooks/useCheckFilters';
import { useQueryParametersState } from 'hooks/useQueryParametersState';
import { CHECK_LIST_STATUS_OPTIONS, CHECKS_PER_PAGE_CARD, CHECKS_PER_PAGE_LIST } from 'components/constants';
import { QueryErrorBoundary } from 'components/QueryErrorBoundary';

import { CheckListItem } from '../CheckListItem';
import { matchesAllFilters } from './checkFilters';
import { CheckListHeader } from './CheckListHeader';
import { CheckListScene } from './CheckListScene';
import { EmptyCheckList } from './EmptyCheckList';
import { CHECK_LIST_STATUS_OPTIONS } from 'page/CheckList/CheckList.constants';
import { useCheckFilters } from 'page/CheckList/CheckList.hooks';
import { matchesAllFilters } from 'page/CheckList/CheckList.utils';
import { CheckListHeader } from 'page/CheckList/components/CheckListHeader';
import { CheckListItem } from 'page/CheckList/components/CheckListItem';
import { CheckListScene } from 'page/CheckList/components/CheckListScene';
import { EmptyCheckList } from 'page/CheckList/components/EmptyCheckList';
ckbedwell marked this conversation as resolved.
Show resolved Hide resolved

const CHECKS_PER_PAGE_CARD = 15;
const CHECKS_PER_PAGE_LIST = 50;

export const CheckList = () => {
const [viewType, setViewType] = useQueryParametersState<CheckListViewType>({
Expand Down
35 changes: 35 additions & 0 deletions src/page/CheckList/CheckList.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { SelectableValue } from '@grafana/data';

import { Check, CheckEnabledStatus, CheckType, DashboardSceneAppConfig } from 'types';

export enum CheckListViewType {
Card = 'card',
List = 'list',
Viz = 'viz',
}

export type ProbeFilter = {
label: string;
value: number;
};

export type CheckTypeFilter = CheckType | 'all';

export type FilterType = 'search' | 'labels' | 'type' | 'status' | 'probes';

export interface CheckFiltersType {
[key: string]: any;

search: string;
labels: string[];
type: CheckTypeFilter;
status: SelectableValue<CheckEnabledStatus>;
probes: Array<SelectableValue<ProbeFilter>>;
}

export interface VizViewSceneAppConfig extends DashboardSceneAppConfig {
checkFilters: CheckFiltersType;
checks: Check[];
onReset: () => void;
onFilterChange: (filters: CheckFiltersType, type: FilterType) => void;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { SelectableValue } from '@grafana/data';

import { Check, CheckEnabledStatus, CheckFiltersType, CheckTypeFilter } from 'types';
import { CheckFiltersType, CheckTypeFilter } from 'page/CheckList/CheckList.types';
import { Check, CheckEnabledStatus } from 'types';
import { getCheckType, matchStrings } from 'utils';
import { CHECK_LIST_STATUS_OPTIONS } from 'page/CheckList/CheckList.constants';

const matchesFilterType = (check: Check, typeFilter: CheckTypeFilter) => {
if (typeFilter === 'all') {
Expand Down Expand Up @@ -71,3 +73,15 @@ export const matchesAllFilters = (check: Check, checkFilters: CheckFiltersType)
matchesSelectedProbes(check, probes)
);
};

export const defaultFilters: CheckFiltersType = {
search: '',
labels: [],
type: 'all',
status: CHECK_LIST_STATUS_OPTIONS[0],
probes: [],
};

export const getDefaultFilters = (): CheckFiltersType => {
return defaultFilters;
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { ROUTES } from 'routing/types';
import { getRoute } from 'routing/utils';
import { useAlertRules } from 'hooks/useAlertRules';
import { useMetricsDS } from 'hooks/useMetricsDS';
import { AlertSensitivityBadge } from 'components/AlertSensitivityBadge';
import { Toggletip } from 'components/Toggletip';
import { AlertSensitivityBadge } from 'page/CheckList/components/AlertSensitivityBadge';

type AlertStatusProps = {
check: Check;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { css } from '@emotion/css';
import { Check } from 'types';
import { useBulkDeleteChecks, useBulkUpdateChecks } from 'data/useChecks';
import { useCanWriteSM } from 'hooks/useDSPermission';
import { BulkEditModal } from 'components/BulkEditModal';
import { BulkActionsModal } from 'page/CheckList/components/BulkActionsModal';

type BulkActionsProps = {
interface BulkActionsProps {
checks: Check[];
onResolved: () => void;
};
}

export enum BulkAction {
enum BulkAction {
Add = `add`,
Remove = `remove`,
}
Expand Down Expand Up @@ -89,7 +89,7 @@ export const BulkActions = ({ checks, onResolved }: BulkActionsProps) => {
</Button>
</div>
{bulkEditAction && (
<BulkEditModal
<BulkActionsModal
checks={checks}
onDismiss={() => setBulkEditAction(null)}
action={bulkEditAction}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { server } from 'test/server';

import { Check } from 'types';

import { BulkEditModal } from './BulkEditModal';
import { BulkActionsModal } from './BulkActionsModal';

const onDismiss = jest.fn();

const renderBulkEditModal = (action: 'add' | 'remove', checks: Check[]) => {
return render(<BulkEditModal onDismiss={onDismiss} checks={checks} action={action} isOpen={true} />);
return render(<BulkActionsModal onDismiss={onDismiss} checks={checks} action={action} isOpen={true} />);
};

test('shows the modal', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { Check, Probe } from 'types';
import { useBulkUpdateChecks } from 'data/useChecks';
import { useProbes } from 'data/useProbes';
import { QueryErrorBoundary } from 'components/QueryErrorBoundary';

import { ProbesByRegion } from './ProbesByRegion';
import { ProbesByRegion } from 'page/CheckList/components/ProbesByRegion';

const actionTypeMap = {
add: {
Expand All @@ -24,26 +23,26 @@ const actionTypeMap = {
},
};

interface BulkEditModalProps {
interface BulkActionModalProps {
onDismiss: () => void;
checks: Check[];
action: 'add' | 'remove';
isOpen: boolean;
}

export const BulkEditModal = (props: BulkEditModalProps) => {
export const BulkActionsModal = (props: BulkActionModalProps) => {
if (!props.action) {
return null;
}

return (
<QueryErrorBoundary>
<BulkEditModalContent {...props} />
<BulkActionsModalContent {...props} />
</QueryErrorBoundary>
);
};

const BulkEditModalContent = ({ onDismiss, isOpen, checks, action }: BulkEditModalProps) => {
const BulkActionsModalContent = ({ onDismiss, isOpen, checks, action }: BulkActionModalProps) => {
const { data: probes = [] } = useProbes();
const { mutate: bulkUpdateChecks, isPending } = useBulkUpdateChecks({ onSuccess: onDismiss });
const [probeIds, setProbeIds] = useState<number[]>([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,24 @@ import { css, cx } from '@emotion/css';

import { Label } from 'types';

interface Props {
interface CheckCardLabelProps {
label: Label;
onLabelSelect: (label: Label) => void;
className?: string;
}

export const CheckCardLabel = ({ label, onLabelSelect, className }: CheckCardLabelProps) => {
const styles = useStyles2(getStyles);

return (
<Tag
onClick={() => onLabelSelect(label)}
name={`${label.name}: ${label.value}`}
className={cx(styles.container, className)}
/>
);
};

const getStyles = (theme: GrafanaTheme2) => ({
container: css`
background-color: #9933cc;
Expand All @@ -21,14 +33,3 @@ const getStyles = (theme: GrafanaTheme2) => ({
text-overflow: ellipsis;
`,
});

export const CheckCardLabel = ({ label, onLabelSelect, className }: Props) => {
const styles = useStyles2(getStyles);
return (
<Tag
onClick={() => onLabelSelect(label)}
name={`${label.name}: ${label.value}`}
className={cx(styles.container, className)}
/>
);
};
Loading