Skip to content

Commit c5d6dca

Browse files
chore(content-explorer): Migrate Content (#3825)
1 parent 19b2cb4 commit c5d6dca

19 files changed

+554
-59
lines changed

src/elements/common/empty-state/EmptyState.js renamed to src/elements/common/empty-state/EmptyState.js.flow

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
/**
2-
* @flow
3-
* @file Empty state component
4-
* @author Box
5-
*/
6-
71
import * as React from 'react';
82
import { FormattedMessage } from 'react-intl';
93
import ErrorEmptyState from '../../../icons/states/ErrorEmptyState';
@@ -18,10 +12,13 @@ import './EmptyState.scss';
1812

1913
type Props = {
2014
isLoading: boolean,
21-
view: View,
15+
view: View
2216
};
2317

24-
const EmptyState = ({ view, isLoading }: Props) => {
18+
const EmptyState = ({
19+
view,
20+
isLoading,
21+
}: Props) => {
2522
let type;
2623
const message =
2724
isLoading && (view === VIEW_FOLDER || view === VIEW_METADATA) ? (

src/elements/common/empty-state/EmptyState.scss

+6
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,10 @@
55
align-items: center;
66
justify-content: center;
77
text-align: center;
8+
9+
svg {
10+
width: 130px;
11+
height: 130px;
12+
margin-bottom: 10px;
13+
}
814
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as React from 'react';
2+
import { FormattedMessage } from 'react-intl';
3+
import { Files, FolderFloat, HatWand, OpenBook } from '@box/blueprint-web-assets/illustrations/Medium';
4+
5+
import messages from '../messages';
6+
import { VIEW_ERROR, VIEW_FOLDER, VIEW_METADATA, VIEW_SEARCH, VIEW_SELECTED } from '../../../constants';
7+
import type { View } from '../../../common/types/core';
8+
9+
import './EmptyState.scss';
10+
11+
export interface EmptyStateProps {
12+
isLoading: boolean;
13+
view: View;
14+
}
15+
16+
const EmptyState = ({ view, isLoading }: EmptyStateProps) => {
17+
let type;
18+
const message =
19+
isLoading && (view === VIEW_FOLDER || view === VIEW_METADATA) ? (
20+
<FormattedMessage {...messages.loadingState} />
21+
) : (
22+
<FormattedMessage {...messages[`${view}State`]} />
23+
);
24+
25+
switch (view) {
26+
case VIEW_ERROR:
27+
type = <HatWand />;
28+
break;
29+
case VIEW_SELECTED:
30+
type = <Files />;
31+
break;
32+
case VIEW_SEARCH:
33+
type = <OpenBook />;
34+
break;
35+
default:
36+
type = <FolderFloat />;
37+
break;
38+
}
39+
40+
return (
41+
<div className="be-empty">
42+
{type}
43+
<div>{message}</div>
44+
</div>
45+
);
46+
};
47+
48+
export default EmptyState;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {default} from './EmptyState';
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
// @flow
21
export { default } from './EmptyState';

src/elements/common/progress-bar/ProgressBar.js renamed to src/elements/common/progress-bar/ProgressBar.js.flow

+17-15
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
1-
/**
2-
* @flow
3-
* @file Progress Bar component
4-
* @author Box
5-
*/
6-
7-
import React, { PureComponent } from 'react';
1+
import React, {PureComponent} from 'react';
82
import './ProgressBar.scss';
93

104
type Props = {
11-
percent: number,
5+
percent: number
126
};
137

148
type State = {
15-
percent: number,
9+
percent: number
1610
};
1711

1812
class ProgressBar extends PureComponent<Props, State> {
1913
props: Props;
2014

2115
state: State;
2216

23-
timeout: TimeoutID;
17+
timeout: number;
2418

25-
interval: IntervalID;
19+
interval: number;
2620

2721
static defaultProps = { percent: 0 };
2822

@@ -33,7 +27,9 @@ class ProgressBar extends PureComponent<Props, State> {
3327
*/
3428
constructor(props: Props) {
3529
super(props);
36-
const { percent }: State = props;
30+
const {
31+
percent,
32+
}: State = props;
3733
this.state = { percent };
3834
}
3935

@@ -61,7 +57,9 @@ class ProgressBar extends PureComponent<Props, State> {
6157
* @return {void}
6258
*/
6359
componentDidUpdate(prevProps: Props): void {
64-
const { percent }: Props = this.props;
60+
const {
61+
percent,
62+
}: Props = this.props;
6563

6664
if (prevProps.percent !== percent) {
6765
this.clearTimeoutAndInterval();
@@ -85,7 +83,9 @@ class ProgressBar extends PureComponent<Props, State> {
8583
* @return {void}
8684
*/
8785
startProgress = () => {
88-
const { percent }: State = this.state;
86+
const {
87+
percent,
88+
}: State = this.state;
8989
if (percent === 0) {
9090
this.interval = setInterval(this.incrementProgress, 100);
9191
} else if (percent === 100) {
@@ -121,7 +121,9 @@ class ProgressBar extends PureComponent<Props, State> {
121121
* @return {void}
122122
*/
123123
render() {
124-
const { percent }: State = this.state;
124+
const {
125+
percent,
126+
}: State = this.state;
125127
const containerStyle = {
126128
opacity: percent > 0 && percent < 100 ? 1 : 0,
127129
transitionDelay: percent > 0 && percent < 100 ? '0' : '0.4s',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React, { useCallback, useEffect, useState, useRef } from 'react';
2+
import './ProgressBar.scss';
3+
4+
export interface ProgressBarProps {
5+
percent: number;
6+
}
7+
8+
const ProgressBar = ({ percent: initialPercent = 0 }: ProgressBarProps) => {
9+
const [percent, setPercent] = useState(initialPercent);
10+
const intervalRef = useRef<number | null>(null);
11+
const timeoutRef = useRef<number | null>(null);
12+
13+
const clearTimeoutAndInterval = () => {
14+
if (intervalRef.current) {
15+
clearInterval(intervalRef.current);
16+
}
17+
if (timeoutRef.current) {
18+
clearTimeout(timeoutRef.current);
19+
}
20+
};
21+
22+
const incrementProgress = () => {
23+
setPercent(prevPercent => {
24+
const newPercent = Math.min(prevPercent + 2 / (prevPercent || 1), 100);
25+
if (newPercent === 100) {
26+
clearTimeoutAndInterval();
27+
}
28+
0;
29+
return newPercent;
30+
});
31+
};
32+
33+
const resetProgress = () => {
34+
setPercent(0);
35+
};
36+
37+
const startProgress = useCallback(() => {
38+
if (percent === 0) {
39+
intervalRef.current = window.setInterval(incrementProgress, 100);
40+
} else if (percent === 100) {
41+
timeoutRef.current = window.setTimeout(resetProgress, 600);
42+
}
43+
// eslint-disable-next-line react-hooks/exhaustive-deps
44+
}, []);
45+
46+
useEffect(() => {
47+
startProgress();
48+
return () => clearTimeoutAndInterval();
49+
}, [percent, startProgress]);
50+
51+
useEffect(() => {
52+
setPercent(initialPercent);
53+
}, [initialPercent]);
54+
55+
const containerStyle = {
56+
opacity: percent > 0 && percent < 100 ? 1 : 0,
57+
transitionDelay: percent > 0 && percent < 100 ? '0' : '0.4s',
58+
} as const;
59+
60+
return (
61+
<div className="be-progress-container" style={containerStyle}>
62+
<div className="be-progress" role="progressbar" style={{ width: `${percent}%` }} />
63+
</div>
64+
);
65+
};
66+
67+
export default ProgressBar;

src/elements/common/progress-bar/__tests__/ProgressBar.test.js

-12
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as React from 'react';
2+
import { act, render, screen } from '../../../../test-utils/testing-library';
3+
import ProgressBar from '../ProgressBar';
4+
5+
describe('ProgressBar', () => {
6+
test('renders with initial percent', () => {
7+
render(<ProgressBar percent={20} />);
8+
const progressBar = screen.getByRole('progressbar');
9+
expect(progressBar).toHaveStyle({ width: '20%' });
10+
});
11+
12+
test('updates percent when props change', () => {
13+
const { rerender } = render(<ProgressBar percent={20} />);
14+
rerender(<ProgressBar percent={30} />);
15+
const progressBar = screen.getByRole('progressbar');
16+
expect(progressBar).toHaveStyle({ width: '30%' });
17+
});
18+
19+
test('resets percent to 0 after reaching 100%', async () => {
20+
jest.useFakeTimers();
21+
render(<ProgressBar percent={0} />);
22+
23+
act(() => {
24+
jest.advanceTimersByTime(250000); // yes it has to be at least 250000ms, any less it will not hit 100%
25+
});
26+
27+
const progressBar = await screen.getByRole('progressbar');
28+
expect(progressBar).toHaveStyle({ width: '100%' });
29+
30+
jest.useRealTimers();
31+
});
32+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {default} from './ProgressBar';
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
// @flow
21
export { default } from './ProgressBar';

src/elements/content-explorer/Content.js renamed to src/elements/content-explorer/Content.js.flow

+14-18
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
/**
2-
* @flow
3-
* @file File picker header and list component
4-
* @author Box
5-
*/
6-
71
import * as React from 'react';
82
import EmptyState from '../common/empty-state';
93
import ProgressBar from '../common/progress-bar';
@@ -25,7 +19,9 @@ import './Content.scss';
2519
* @return {boolean} empty or not
2620
*/
2721
function isEmpty(view: View, currentCollection: Collection, fieldsToShow: FieldsToShow): boolean {
28-
const { items = [] }: Collection = currentCollection;
22+
const {
23+
items = [],
24+
}: Collection = currentCollection;
2925
return view === VIEW_ERROR || !items.length || (view === VIEW_METADATA && !fieldsToShow.length);
3026
}
3127

@@ -42,21 +38,21 @@ type Props = {
4238
isMedium: boolean,
4339
isSmall: boolean,
4440
isTouch: boolean,
45-
onItemClick: Function,
46-
onItemDelete: Function,
47-
onItemDownload: Function,
48-
onItemPreview: Function,
49-
onItemRename: Function,
50-
onItemSelect: Function,
51-
onItemShare: Function,
52-
onMetadataUpdate: Function,
53-
onSortChange: Function,
41+
onItemClick: any,
42+
onItemDelete: any,
43+
onItemDownload: any,
44+
onItemPreview: any,
45+
onItemRename: any,
46+
onItemSelect: any,
47+
onItemShare: any,
48+
onMetadataUpdate: any,
49+
onSortChange: any,
5450
rootElement?: HTMLElement,
5551
rootId: string,
5652
selected?: BoxItem,
57-
tableRef: Function,
53+
tableRef: any,
5854
view: View,
59-
viewMode?: ViewMode,
55+
viewMode?: ViewMode
6056
};
6157

6258
const Content = ({

0 commit comments

Comments
 (0)