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

CNV-32097: add column management to the custom list filter #1578

Merged
merged 1 commit into from
Oct 12, 2023
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
5 changes: 3 additions & 2 deletions cypress/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
],
"plugins": ["cypress", "chai-friendly"],
"rules": {
"cypress/no-force": "error",
"cypress/assertion-before-screenshot": "error"
"chai-friendly/no-unused-expressions": "off",
"cypress/assertion-before-screenshot": "error",
"cypress/no-force": "error"
}
}
24 changes: 12 additions & 12 deletions cypress/cypress.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"viewportWidth": 1920,
"viewportHeight": 1080,
"defaultCommandTimeout": 40000,
"fixturesFolder": false,
"integrationFolder": "tests",
"screenshotsFolder": "./gui-test-screenshots/screenshots/",
"videosFolder": "./gui-test-screenshots/videos/",
"video": true,
"pluginsFile": "plugin.js",
"reporter": "../node_modules/cypress-multi-reporters",
"reporterOptions": {
"configFile": "reporter-config.json"
},
"retries": {
"openMode": 0,
"runMode": 1
},
"screenshotOnRunFailure": true,
"screenshotsFolder": "./gui-test-screenshots/screenshots/",
"supportFile": "support.ts",
"pluginsFile": "plugin.js",
"fixturesFolder": false,
"defaultCommandTimeout": 30000,
"retries": {
"runMode": 1,
"openMode": 0
}
"video": true,
"videosFolder": "./gui-test-screenshots/videos/",
"viewportHeight": 1080,
"viewportWidth": 1920
}
10 changes: 10 additions & 0 deletions locales/en/plugin__kubevirt-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
"Add tolerations to allow a VirtualMachine to schedule onto Nodes with matching taints.": "Add tolerations to allow a VirtualMachine to schedule onto Nodes with matching taints.",
"Add volume": "Add volume",
"Adding a bridged network interface to a running VirtualMachine is a Technology Preview feature that allows you to choose between live-migrating the VirtualMachine or restarting it to apply the changes. Enabling this feature requires cluster admin permissions.": "Adding a bridged network interface to a running VirtualMachine is a Technology Preview feature that allows you to choose between live-migrating the VirtualMachine or restarting it to apply the changes. Enabling this feature requires cluster admin permissions.",
"Additional column list": "Additional column list",
"Additional columns": "Additional columns",
"Additional disks types and interfaces are available when the VirtualMachine is stopped.": "Additional disks types and interfaces are available when the VirtualMachine is stopped.",
"Additional resources": "Additional resources",
"Additional statuses": "Additional statuses",
Expand Down Expand Up @@ -248,6 +250,7 @@
"Cluster administrator permissions are required to enable this feature.": "Cluster administrator permissions are required to enable this feature.",
"Cluster scope migrations": "Cluster scope migrations",
"Collapse": "Collapse",
"Column management": "Column management",
"Completion timeout": "Completion timeout",
"CompletionTimeoutPerGiB is the maximum number of seconds per GiB a migration is allowed to take. If a live-migration takes longer to migrate than this value multiplied by the size of the VMI, the migration will be cancelled, unless AllowPostCopy is true. Defaults to 800. ": "CompletionTimeoutPerGiB is the maximum number of seconds per GiB a migration is allowed to take. If a live-migration takes longer to migrate than this value multiplied by the size of the VMI, the migration will be cancelled, unless AllowPostCopy is true. Defaults to 800. ",
"Compute-intensive applications": "Compute-intensive applications",
Expand Down Expand Up @@ -337,6 +340,8 @@
"Decrement": "Decrement",
"Dedicated resources": "Dedicated resources",
"Default": "Default",
"Default {{resourceKind}} columns": "Default {{resourceKind}} columns",
"Default column list": "Default column list",
"Default InstanceType": "Default InstanceType",
"Default templates": "Default templates",
"Default value for this parameter": "Default value for this parameter",
Expand Down Expand Up @@ -605,6 +610,7 @@
"Make persistent disk": "Make persistent disk",
"Make persistent?": "Make persistent?",
"Make sure to have clone permissions in the destination namespace. <2>Learn more <1></1></2>": "Make sure to have clone permissions in the destination namespace. <2>Learn more <1></1></2>",
"Manage columns": "Manage columns",
"Manage source": "Manage source",
"Manage source for {{dataSource}}": "Manage source for {{dataSource}}",
"Manage SSH keys": "Manage SSH keys",
Expand Down Expand Up @@ -879,6 +885,7 @@
"Restart": "Restart",
"Restart the VirtualMachine to apply changes.": "Restart the VirtualMachine to apply changes.",
"Restore": "Restore",
"Restore default columns": "Restore default columns",
"Restore is enabled only for offline VirtualMachine.": "Restore is enabled only for offline VirtualMachine.",
"Restore snapshot": "Restore snapshot",
"Restore template settings": "Restore template settings",
Expand Down Expand Up @@ -934,6 +941,7 @@
"Select StorageClass": "Select StorageClass",
"Select volume to boot from": "Select volume to boot from",
"Select workloads that must have all the following expressions.": "Select workloads that must have all the following expressions.",
"Selected columns will appear in the table.": "Selected columns will appear in the table.",
"Selected StorageClass is different from StorageClass of the source": "Selected StorageClass is different from StorageClass of the source",
"Selected sysprep": "Selected sysprep",
"Selector": "Selector",
Expand Down Expand Up @@ -1060,6 +1068,7 @@
"The following information regarding how the disks are partitioned is provided by the guest agent.": "The following information regarding how the disks are partitioned is provided by the guest agent.",
"The following resources will be deleted along with this VirtualMachine. Unchecked items will not be deleted.": "The following resources will be deleted along with this VirtualMachine. Unchecked items will not be deleted.",
"The machine type defines the virtual hardware configuration while the operating system name and version refer to the hypervisor.": "The machine type defines the virtual hardware configuration while the operating system name and version refer to the hypervisor.",
"The namespace column is only shown when in \"All projects\"": "The namespace column is only shown when in \"All projects\"",
"The preferred VirtualMachine attribute values required to run a given workload.": "The preferred VirtualMachine attribute values required to run a given workload.",
"The template has": "The template has",
"The virtctl client is a supplemental command-line utility for managing virtualization resources from the command line.": "The virtctl client is a supplemental command-line utility for managing virtualization resources from the command line.",
Expand Down Expand Up @@ -1258,6 +1267,7 @@
"You can edit the boot order in the <1>{t('Disks tab')}</1>": "You can edit the boot order in the <1>{t('Disks tab')}</1>",
"You can host and manage virtualized workloads on the same platform as container-based workloads.": "You can host and manage virtualized workloads on the same platform as container-based workloads.",
"You can select the boot source in the <2>Disks</2> tab.": "You can select the boot source in the <2>Disks</2> tab.",
"You can select up to {{MAX_VIEW_COLS}} columns": "You can select up to {{MAX_VIEW_COLS}} columns",
"You can use cloud-init to initialize the operating system with a specific configuration when the VirtualMachine is started.": "You can use cloud-init to initialize the operating system with a specific configuration when the VirtualMachine is started.",
"You must ensure that the configuration is correct before starting the VirtualMachine.": "You must ensure that the configuration is correct before starting the VirtualMachine.",
"You're in view-only mode": "You're in view-only mode"
Expand Down
47 changes: 47 additions & 0 deletions src/utils/components/ColumnManagementModal/ColumnManagement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { FC } from 'react';

import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { isEmpty } from '@kubevirt-utils/utils/utils';
import { ColumnLayout } from '@openshift-console/dynamic-plugin-sdk';
import { Button, ToolbarGroup, ToolbarItem, Tooltip } from '@patternfly/react-core';
import { ColumnsIcon } from '@patternfly/react-icons';

import { ColumnManagementModal } from '../ColumnManagementModal/ColumnManagementModal';
import { useModal } from '../ModalProvider/ModalProvider';

type ColumnManagementProps = {
columnLayout: ColumnLayout;
hideColumnManagement?: boolean;
};

const ColumnManagement: FC<ColumnManagementProps> = ({ columnLayout, hideColumnManagement }) => {
const { t } = useKubevirtTranslation();
const { createModal } = useModal();

if (isEmpty(columnLayout) || !columnLayout?.id || hideColumnManagement) {
return null;
}

return (
<ToolbarGroup>
<ToolbarItem>
<Tooltip content={t('Manage columns')} trigger="mouseenter">
<Button
onClick={() =>
createModal((props) => (
<ColumnManagementModal {...props} columnLayout={columnLayout} />
))
}
aria-label={t('Column management')}
data-test="manage-columns"
variant="plain"
>
<ColumnsIcon />
</Button>
</Tooltip>
</ToolbarItem>
</ToolbarGroup>
);
};

export default ColumnManagement;
193 changes: 193 additions & 0 deletions src/utils/components/ColumnManagementModal/ColumnManagementModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import React, { FC, MouseEventHandler, SyntheticEvent, useState } from 'react';

import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import useKubevirtUserSettingsTableColumns from '@kubevirt-utils/hooks/useKubevirtUserSettings/useKubevirtUserSettingsTableColumns';
import { ColumnLayout } from '@openshift-console/dynamic-plugin-sdk';
import {
ActionList,
ActionListItem,
Alert,
AlertVariant,
Button,
ButtonVariant,
DataList,
Form,
Modal,
ModalVariant,
Stack,
StackItem,
} from '@patternfly/react-core';

import { MAX_VIEW_COLS } from './constants';
import DataListRow from './DataListRow';
import { createInputId, getColumnId } from './utils';

import './column-management-modal.scss';

type ColumnManagementModalProps = {
columnLayout: ColumnLayout;
isOpen: boolean;
onClose: () => void;
};

export const ColumnManagementModal: FC<ColumnManagementModalProps> = ({
columnLayout,
upalatucci marked this conversation as resolved.
Show resolved Hide resolved
isOpen,
onClose,
}) => {
const { t } = useKubevirtTranslation();
const { columns, id, selectedColumns, showNamespaceOverride, type } = columnLayout;

const defaultColumns = columns.filter((column) => column.id && !column.additional);
const additionalColumns = columns.filter((column) => column.additional);

const [_, setActiveColumns, loaded, error] = useKubevirtUserSettingsTableColumns({
columnManagementID: id,
columns,
});

const [checkedColumns, setCheckedColumns] = useState<Set<string>>(
selectedColumns && selectedColumns.size !== 0
? new Set(selectedColumns)
: new Set(defaultColumns.map((col) => col.id)),
);

const onColumnChange = (checked: boolean, event: SyntheticEvent): void => {
const updatedCheckedColumns = new Set<string>(checkedColumns);
const selectedId = getColumnId(event?.currentTarget?.id);
updatedCheckedColumns.has(selectedId)
? updatedCheckedColumns.delete(selectedId)
: updatedCheckedColumns.add(selectedId);
setCheckedColumns(updatedCheckedColumns);
};

const submit: MouseEventHandler<HTMLButtonElement> = async (event) => {
event.preventDefault();
const orderedCheckedColumns = new Set<string>();
checkedColumns.forEach((ids) => orderedCheckedColumns.add(ids));

await setActiveColumns([...orderedCheckedColumns]);
onClose();
};

const areMaxColumnsDisplayed = checkedColumns.size >= MAX_VIEW_COLS;

const resetColumns = (event: SyntheticEvent): void => {
event.preventDefault();
const updatedCheckedColumns = new Set(checkedColumns);
defaultColumns.forEach((col) => col.id && updatedCheckedColumns.add(col.id));
additionalColumns.forEach((col) => updatedCheckedColumns.delete(col.id));
setCheckedColumns(updatedCheckedColumns);
};

return (
<Modal
footer={
<Stack className="kv-tabmodal-footer" hasGutter>
{error && (
<StackItem>
<Alert isInline title={t('An error occurred')} variant={AlertVariant.danger}>
<Stack hasGutter>
<StackItem>{error.message}</StackItem>
</Stack>
</Alert>
</StackItem>
)}
<StackItem>
<ActionList className="column-management-modal__action-list">
<ActionListItem>
<Button key="reset" onClick={resetColumns} variant={ButtonVariant.link}>
{t('Restore default columns')}
</Button>
</ActionListItem>

<ActionListItem>
<Button onClick={onClose} variant={ButtonVariant.secondary}>
{t('Cancel')}
</Button>
</ActionListItem>
<ActionListItem>
<Button
form="modal-with-form-form"
isDisabled={!loaded}
isLoading={!loaded}
key="create"
onClick={submit}
variant={ButtonVariant.primary}
>
{t('Save')}
</Button>
</ActionListItem>
</ActionList>
</StackItem>
</Stack>
}
isOpen={isOpen}
onClose={onClose}
position="top"
title={t('Manage columns')}
variant={ModalVariant.medium}
>
<Form className="modal-content" name="form">
<div>
<p>{t('Selected columns will appear in the table.')}</p>
</div>
<div>
<Alert
className="co-alert"
isInline
title={t('You can select up to {{MAX_VIEW_COLS}} columns', { MAX_VIEW_COLS })}
variant={AlertVariant.info}
>
{!showNamespaceOverride &&
t('The namespace column is only shown when in "All projects"')}
</Alert>
</div>
<div className="row co-m-form-row">
<div className="col-sm-12">
<span className="col-sm-6">
<label className="control-label">
{t('Default {{resourceKind}} columns', { resourceKind: type })}
</label>
<DataList
aria-label={t('Default column list')}
id="defalt-column-management"
isCompact
>
{defaultColumns.map((defaultColumn) => (
<DataListRow
checkedColumns={checkedColumns}
column={defaultColumn}
disableUncheckedRow={areMaxColumnsDisplayed}
inputId={createInputId(defaultColumn.id)}
key={defaultColumn.id}
onChange={onColumnChange}
/>
))}
</DataList>
</span>
<span className="col-sm-6">
<label className="control-label">{t('Additional columns')}</label>
<DataList
aria-label={t('Additional column list')}
id="additional-column-management"
isCompact
>
{additionalColumns.map((additionalColumn) => (
<DataListRow
checkedColumns={checkedColumns}
column={additionalColumn}
disableUncheckedRow={areMaxColumnsDisplayed}
inputId={createInputId(additionalColumn.id)}
key={additionalColumn.id}
onChange={onColumnChange}
/>
))}
</DataList>
</span>
</div>
</div>
</Form>
</Modal>
);
};
Loading
Loading