Skip to content

Commit

Permalink
[ui-storageBrowser] Implement UI modal for content summary (#3681)
Browse files Browse the repository at this point in the history
  • Loading branch information
nidhibhatg authored May 14, 2024
1 parent f678bec commit c6673f8
Show file tree
Hide file tree
Showing 16 changed files with 640 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,6 @@ import '@testing-library/jest-dom';

import InputModal from './InputModal';

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn()
}))
});

describe('InputModal', () => {
test('renders custom modal title', () => {
const inputModal = render(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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.
@use 'mixins';
@use 'variables' as vars;
$action-dropdown-width: 214px;

//TODO: Remove styling for cuix button
.hue-storage-browser__table-actions-btn {
box-shadow: none;
background-color: transparent;
border: none;
}

.hue-storage-browser__table-actions-dropdown {
align-items: center;
width: $action-dropdown-width;
@include mixins.hue-svg-icon__d3-conflict;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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 React from 'react';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';

import StorageBrowserRowActions from './StorageBrowserRowActions';
import { StorageBrowserTableData } from '../../../../reactComponents/FileChooser/types';

describe('StorageBrowserRowActions', () => {
//View summary option is enabled and added to the actions menu when the row data is either hdfs/ofs and a single file
const mockRecord: StorageBrowserTableData = {
name: 'test',
size: '0\u00a0bytes',
user: 'demo',
group: 'demo',
permission: 'drwxr-xr-x',
mtime: 'May 12, 2024 10:37 PM',
type: '',
path: ''
};
test('renders view summary option when record is a hdfs file', async () => {
const onViewSummary = jest.fn();
const user = userEvent.setup();
mockRecord.path = '/user/demo/test';
mockRecord.type = 'file';
const { getByRole, queryByRole } = render(
<StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
);
await user.click(getByRole('button'));
expect(queryByRole('menuitem', { name: 'View Summary' })).not.toBeNull();
});

test('renders view summary option when record is a ofs file', async () => {
const onViewSummary = jest.fn();
const user = userEvent.setup();
mockRecord.path = 'ofs://demo/test';
mockRecord.type = 'file';
const { getByRole, queryByRole } = render(
<StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
);
await user.click(getByRole('button'));
expect(queryByRole('menuitem', { name: 'View Summary' })).not.toBeNull();
});

test('does not render view summary option when record is a hdfs folder', async () => {
const onViewSummary = jest.fn();
const user = userEvent.setup();
mockRecord.path = '/user/demo/test';
mockRecord.type = 'dir';
const { getByRole, queryByRole } = render(
<StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
);
await user.click(getByRole('button'));
expect(queryByRole('menuitem', { name: 'View Summary' })).toBeNull();
});

test('does not render view summary option when record is a an abfs file', async () => {
const onViewSummary = jest.fn();
const user = userEvent.setup();
mockRecord.path = 'abfs://demo/test';
mockRecord.type = 'file';
const { getByRole, queryByRole } = render(
<StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
);
await user.click(getByRole('button'));
expect(queryByRole('menuitem', { name: 'View Summary' })).toBeNull();
});

test('calls onViewSummary after View summary menu option is clicked', async () => {
const onViewSummary = jest.fn();
const user = userEvent.setup();
mockRecord.path = '/user/demo/test';
mockRecord.type = 'file';
const { getByRole } = render(
<StorageBrowserRowActions rowData={mockRecord} onViewSummary={onViewSummary} />
);
await user.click(getByRole('button'));
expect(onViewSummary).not.toBeCalled();
await user.click(getByRole('menuitem', { name: 'View Summary' }));
expect(onViewSummary).toBeCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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 React from 'react';
import { Dropdown } from 'antd';
import { MenuItemType } from 'antd/lib/menu/hooks/useItems';

import { BorderlessButton } from 'cuix/dist/components/Button';
import MoreVerticalIcon from '@cloudera/cuix-core/icons/react/MoreVerticalIcon';
import InfoIcon from '@cloudera/cuix-core/icons/react/InfoIcon';

import { i18nReact } from '../../../../utils/i18nReact';
import { StorageBrowserTableData } from '../../../../reactComponents/FileChooser/types';
import { isHDFS, isOFS } from '../../../../../js/utils/storageBrowserUtils';

import './StorageBrowserRowActions.scss';

interface StorageBrowserRowActionsProps {
rowData: StorageBrowserTableData;
onViewSummary: (selectedFilePath: string) => void;
}

const StorageBrowserRowActions = ({
rowData,
onViewSummary
}: StorageBrowserRowActionsProps): JSX.Element => {
const { t } = i18nReact.useTranslation();

//TODO: handle multiple file selection scenarios
const isSummaryEnabled = () =>
(isHDFS(rowData.path) || isOFS(rowData.path)) && rowData.type === 'file';

const getActions = () => {
const actions: MenuItemType[] = [];
if (isSummaryEnabled()) {
actions.push({
key: 'content_summary',
icon: <InfoIcon />,
label: t('View Summary'),
onClick: () => {
onViewSummary(rowData.path);
}
});
}
return actions;
};

return (
<Dropdown
overlayClassName="hue-storage-browser__table-actions-dropdown"
menu={{
items: getActions(),
className: 'hue-storage-browser__table-actions-menu'
}}
trigger={['click']}
>
<BorderlessButton
onClick={e => e.stopPropagation()}
className="hue-storage-browser__table-actions-btn"
data-event=""
icon={<MoreVerticalIcon />}
/>
</Dropdown>
);
};

export default StorageBrowserRowActions;
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import {
SortOrder
} from '../../../../reactComponents/FileChooser/types';
import Pagination from '../../../../reactComponents/Pagination/Pagination';
import StorageBrowserRowActions from '../StorageBrowserRowActions/StorageBrowserRowActions';
import './StorageBrowserTable.scss';
import Tooltip from 'antd/es/tooltip';
import SummaryModal from '../../SummaryModal/SummaryModal';

interface StorageBrowserTableProps {
className?: string;
Expand Down Expand Up @@ -72,6 +74,9 @@ const StorageBrowserTable: React.FC<StorageBrowserTableProps> = ({
...restProps
}): JSX.Element => {
const [tableHeight, setTableHeight] = useState<number>();
const [showSummaryModal, setShowSummaryModal] = useState<boolean>(false);
//TODO: accept multiple files and folder select
const [selectedFile, setSelectedFile] = useState<string>('');

const { t } = i18nReact.useTranslation();

Expand All @@ -90,6 +95,11 @@ const StorageBrowserTable: React.FC<StorageBrowserTableProps> = ({
}
};

const onViewSummary = (filePath: string) => {
setSelectedFile(filePath);
setShowSummaryModal(true);
};

const getColumns = (file: StorageBrowserTableData) => {
const columns: ColumnProps<StorageBrowserTableData>[] = [];
for (const [key] of Object.entries(file)) {
Expand Down Expand Up @@ -126,10 +136,19 @@ const StorageBrowserTable: React.FC<StorageBrowserTableProps> = ({
</Tooltip>
);
} else {
column.width = key === 'mtime' ? '15%' : '10%';
column.width = key === 'mtime' ? '15%' : '9%';
}
columns.push(column);
}
columns.push({
dataIndex: 'actions',
title: '',
key: 'actions',
render: (_, record: StorageBrowserTableData) => (
<StorageBrowserRowActions onViewSummary={onViewSummary} rowData={record} />
),
width: '4%'
});
return columns.filter(col => col.dataIndex !== 'type' && col.dataIndex !== 'path');
};

Expand Down Expand Up @@ -212,6 +231,11 @@ const StorageBrowserTable: React.FC<StorageBrowserTableProps> = ({
pageSize={pageSize}
pageStats={pageStats}
/>
<SummaryModal
showModal={showSummaryModal}
path={selectedFile}
onClose={() => setShowSummaryModal(false)}
></SummaryModal>
</>
);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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.
@use 'variables' as vars;

.hue-summary-modal__row.ant-row {
margin-bottom: vars.$fluidx-spacing-s;

&:last-child {
margin-bottom: 0;
}
}
Loading

0 comments on commit c6673f8

Please sign in to comment.