Skip to content

Commit cce0bae

Browse files
[ui-storagebrowser] add useWindowSize hook to get window size (#3930)
1 parent 488750b commit cce0bae

File tree

4 files changed

+242
-114
lines changed

4 files changed

+242
-114
lines changed

desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageDirectoryPage.scss

Lines changed: 63 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,82 +13,94 @@
1313
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1414
// See the License for the specific language governing permissions and
1515
// limitations under the License.
16+
1617
@use 'variables' as vars;
1718
@use 'mixins';
1819

1920
$cell-height: 40px;
2021
$table-placeholder-height: 100px;
21-
$action-dropdown-width: 214px;
2222
$icon-margin: 5px;
2323

2424
.antd.cuix {
25-
.hue-storage-browser__actions-bar {
25+
.hue-storage-browser-directory {
2626
display: flex;
27-
margin: vars.$fluidx-spacing-s 0 vars.$fluidx-spacing-s vars.$fluidx-spacing-xxs;
28-
justify-content: space-between;
29-
}
30-
31-
.hue-storage-browser__search {
32-
width: 30%;
27+
flex-direction: column;
28+
flex: 1;
29+
30+
&__actions-bar {
31+
display: flex;
32+
margin: vars.$fluidx-spacing-s 0 vars.$fluidx-spacing-s vars.$fluidx-spacing-xxs;
33+
justify-content: space-between;
34+
35+
&__search {
36+
width: min(300px, calc(100% - 200px));
37+
38+
.ant-input {
39+
box-shadow: none;
40+
}
41+
}
42+
43+
&__actions {
44+
display: flex;
45+
gap: vars.$fluidx-spacing-xs;
46+
47+
.cdp-icon-dropdown {
48+
margin-left: $icon-margin;
49+
}
50+
}
51+
}
3352

34-
.ant-input {
35-
box-shadow: none;
53+
&__table-container {
54+
flex: 1;
55+
display: flex;
56+
flex-direction: column;
3657
}
37-
}
3858

39-
.hue-storage-browser__actions-bar-right {
40-
display: flex;
41-
gap: vars.$fluidx-spacing-xs;
59+
.hue-storage-browser__table {
60+
.ant-table-placeholder {
61+
height: $table-placeholder-height;
62+
text-align: center;
63+
}
4264

43-
.cdp-icon-dropdown {
44-
margin-left: $icon-margin;
65+
th.ant-table-cell {
66+
height: $cell-height;
67+
background-color: vars.$fluidx-gray-040;
68+
}
4569
}
46-
}
4770

48-
.hue-storage-browser__table {
49-
.ant-table-placeholder {
50-
height: $table-placeholder-height;
51-
text-align: center;
71+
.hue-storage-browser__table-cell-icon {
72+
color: vars.$fluidx-blue-700;
73+
margin-right: 6px;
74+
vertical-align: middle;
5275
}
5376

54-
th.ant-table-cell {
55-
height: $cell-height;
56-
background-color: vars.$fluidx-gray-040;
57-
}
58-
}
77+
.hue-storage-browser__table-row {
78+
border-bottom: 1px solid vars.$fluidx-gray-040;
5979

60-
.hue-storage-browser__table-cell-icon {
61-
color: vars.$fluidx-blue-700;
62-
margin-right: 6px;
63-
vertical-align: middle;
64-
}
80+
:hover {
81+
cursor: pointer;
82+
}
6583

66-
.hue-storage-browser__table-row {
67-
border-bottom: 1px solid vars.$fluidx-gray-040;
84+
td.ant-table-cell {
85+
height: $cell-height;
86+
@include mixins.nowrap-ellipsis;
87+
}
6888

69-
:hover {
70-
cursor: pointer;
89+
td.ant-table-cell:first-child {
90+
text-overflow: initial;
91+
}
7192
}
7293

73-
td.ant-table-cell {
74-
height: $cell-height;
75-
@include mixins.nowrap-ellipsis;
94+
.hue-storage-browser__table-cell-name {
95+
color: vars.$fluidx-blue-700;
7696
}
7797

78-
td.ant-table-cell:first-child {
79-
text-overflow: initial;
98+
.hue-storage-browser__table-column-header {
99+
display: flex;
80100
}
81-
}
82-
83-
.hue-storage-browser__table-cell-name {
84-
color: vars.$fluidx-blue-700;
85-
}
86101

87-
.hue-storage-browser__table-column-header {
88-
display: flex;
89-
}
90-
91-
.hue-storage-browser__table-column-title {
92-
text-transform: capitalize;
102+
.hue-storage-browser__table-column-title {
103+
text-transform: capitalize;
104+
}
93105
}
94106
}

desktop/core/src/desktop/js/apps/storageBrowser/StorageDirectoryPage/StorageDirectoryPage.tsx

Lines changed: 46 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// See the License for the specific language governing permissions and
1515
// limitations under the License.
1616

17-
import React, { useMemo, useState, useCallback, useEffect } from 'react';
17+
import React, { useMemo, useState, useCallback } from 'react';
1818
import { ColumnProps } from 'antd/lib/table';
1919
import { Input, Tooltip } from 'antd';
2020

@@ -52,6 +52,7 @@ import DragAndDrop from '../../../reactComponents/DragAndDrop/DragAndDrop';
5252
import UUID from '../../../utils/string/UUID';
5353
import { UploadItem } from '../../../utils/hooks/useFileUpload/util';
5454
import FileUploadQueue from '../../../reactComponents/FileUploadQueue/FileUploadQueue';
55+
import { useWindowSize } from '../../../utils/hooks/useWindowSize/useWindowSize';
5556
import LoadingErrorWrapper from '../../../reactComponents/LoadingErrorWrapper/LoadingErrorWrapper';
5657
import StorageDirectoryActions from './StorageDirectoryActions/StorageDirectoryActions';
5758

@@ -82,7 +83,6 @@ const StorageDirectoryPage = ({
8283
...restProps
8384
}: StorageDirectoryPageProps): JSX.Element => {
8485
const [loadingFiles, setLoadingFiles] = useState<boolean>(false);
85-
const [tableHeight, setTableHeight] = useState<number>(100);
8686
const [selectedFiles, setSelectedFiles] = useState<StorageDirectoryTableData[]>([]);
8787
const [filesToUpload, setFilesToUpload] = useState<UploadItem[]>([]);
8888
const [polling, setPolling] = useState<boolean>(false);
@@ -237,28 +237,9 @@ const StorageDirectoryPage = ({
237237
setFilesToUpload(prevFiles => [...prevFiles, ...newUploadItems]);
238238
};
239239

240-
useEffect(() => {
241-
//TODO: handle table resize
242-
const calculateTableHeight = () => {
243-
const windowHeight = window.innerHeight;
244-
// TODO: move 450 to dynamic based on table header height, tab nav and some header.
245-
const tableHeightFix = windowHeight - 450;
246-
return tableHeightFix;
247-
};
248-
249-
const handleWindowResize = () => {
250-
const tableHeight = calculateTableHeight();
251-
setTableHeight(tableHeight);
252-
};
253-
254-
handleWindowResize(); // Calculate initial scroll height
255-
256-
window.addEventListener('resize', handleWindowResize);
257-
258-
return () => {
259-
window.removeEventListener('resize', handleWindowResize);
260-
};
261-
}, []);
240+
const [tableRef, rect] = useWindowSize();
241+
// 40px for table header, 50px for pagination
242+
const tableBodyHeight = Math.max(rect.height - 90, 100);
262243

263244
const locale = {
264245
emptyText: t('Folder is empty')
@@ -274,18 +255,18 @@ const StorageDirectoryPage = ({
274255
];
275256

276257
return (
277-
<>
278-
<div className="hue-storage-browser__actions-bar">
258+
<div className="hue-storage-browser-directory">
259+
<div className="hue-storage-browser-directory__actions-bar">
279260
<Input
280-
className="hue-storage-browser__search"
261+
className="hue-storage-browser-directory__actions-bar__search"
281262
placeholder={t('Search')}
282263
allowClear={true}
283264
onChange={event => {
284265
handleSearch(event.target.value);
285266
}}
286267
disabled={!tableData.length && !searchTerm.length}
287268
/>
288-
<div className="hue-storage-browser__actions-bar-right">
269+
<div className="hue-storage-browser-directory__actions-bar__actions">
289270
<StorageDirectoryActions
290271
fileStats={fileStats}
291272
fileSystem={fileSystem}
@@ -301,41 +282,43 @@ const StorageDirectoryPage = ({
301282
</div>
302283
</div>
303284

304-
<DragAndDrop onDrop={onFilesDrop}>
305-
<LoadingErrorWrapper
306-
loading={(loadingFiles || listDirectoryLoading) && !polling}
307-
errors={errorConfig}
308-
>
309-
<Table
310-
className={className}
311-
columns={getColumns(tableData[0] ?? {})}
312-
dataSource={tableData}
313-
onRow={onRowClicked}
314-
pagination={false}
315-
rowClassName={rowClassName}
316-
rowKey={r => `${r.path}_${r.type}_${r.mtime}`}
317-
rowSelection={{
318-
hideSelectAll: !tableData.length,
319-
columnWidth: 36,
320-
type: 'checkbox',
321-
...rowSelection
322-
}}
323-
scroll={{ y: tableHeight }}
324-
data-testid={testId}
325-
locale={locale}
326-
{...restProps}
327-
/>
328-
329-
{filesData?.page && filesData?.page?.total_pages > 0 && (
330-
<Pagination
331-
setPageSize={setPageSize}
332-
pageSize={pageSize}
333-
setPageNumber={setPageNumber}
334-
pageStats={filesData?.page}
285+
<div ref={tableRef} className="hue-storage-browser-directory__table-container">
286+
<DragAndDrop onDrop={onFilesDrop}>
287+
<LoadingErrorWrapper
288+
loading={(loadingFiles || listDirectoryLoading) && !polling}
289+
errors={errorConfig}
290+
>
291+
<Table
292+
className={className}
293+
columns={getColumns(tableData[0] ?? {})}
294+
dataSource={tableData}
295+
onRow={onRowClicked}
296+
pagination={false}
297+
rowClassName={rowClassName}
298+
rowKey={r => `${r.path}_${r.type}_${r.mtime}`}
299+
rowSelection={{
300+
hideSelectAll: !tableData.length,
301+
columnWidth: 36,
302+
type: 'checkbox',
303+
...rowSelection
304+
}}
305+
scroll={{ y: tableBodyHeight }}
306+
data-testid={`${testId}`}
307+
locale={locale}
308+
{...restProps}
335309
/>
336-
)}
337-
</LoadingErrorWrapper>
338-
</DragAndDrop>
310+
311+
{filesData?.page && filesData?.page?.total_pages > 0 && (
312+
<Pagination
313+
setPageSize={setPageSize}
314+
pageSize={pageSize}
315+
setPageNumber={setPageNumber}
316+
pageStats={filesData?.page}
317+
/>
318+
)}
319+
</LoadingErrorWrapper>
320+
</DragAndDrop>
321+
</div>
339322
{filesToUpload.length > 0 && (
340323
<FileUploadQueue
341324
filesQueue={filesToUpload}
@@ -346,7 +329,7 @@ const StorageDirectoryPage = ({
346329
}}
347330
/>
348331
)}
349-
</>
332+
</div>
350333
);
351334
};
352335

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Licensed to Cloudera, Inc. under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. Cloudera, Inc. licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
import { act, renderHook } from '@testing-library/react';
18+
import { useWindowSize } from './useWindowSize';
19+
20+
describe('useWindowSize', () => {
21+
const mockCallback = jest.fn();
22+
const mockObserver = {
23+
observe: jest.fn(),
24+
disconnect: jest.fn()
25+
};
26+
27+
beforeAll(() => {
28+
global.ResizeObserver = jest.fn().mockImplementation(callback => {
29+
mockCallback.mockImplementation(callback);
30+
return mockObserver;
31+
});
32+
});
33+
34+
beforeEach(() => {
35+
jest.clearAllMocks();
36+
});
37+
38+
it('should initialize with default dimensions', () => {
39+
const { result } = renderHook(() => useWindowSize());
40+
41+
expect(result.current[1]).toEqual({
42+
x: 0,
43+
y: 0,
44+
width: 0,
45+
height: 0,
46+
top: 0,
47+
right: 0,
48+
bottom: 0,
49+
left: 0
50+
});
51+
});
52+
53+
it('should update dimensions when ResizeObserver fires', () => {
54+
const { result } = renderHook(() => useWindowSize());
55+
const div = document.createElement('div');
56+
result.current[0].current = div;
57+
58+
const newDimensions = {
59+
x: 10,
60+
y: 20,
61+
width: 100,
62+
height: 200,
63+
top: 20,
64+
right: 110,
65+
bottom: 220,
66+
left: 10
67+
};
68+
69+
act(() => {
70+
mockCallback([{ target: div, contentRect: newDimensions }]);
71+
});
72+
73+
expect(result.current[1]).toEqual(newDimensions);
74+
});
75+
76+
it('should disconnect observer on unmount', () => {
77+
const { unmount } = renderHook(() => useWindowSize());
78+
79+
unmount();
80+
expect(mockObserver.disconnect).toHaveBeenCalled();
81+
});
82+
});

0 commit comments

Comments
 (0)