Skip to content

Commit

Permalink
update workspace list (opensearch-project#259)
Browse files Browse the repository at this point in the history
* feat: update workspace list

Signed-off-by: tygao <tygao@amazon.com>

* test: remove failed snapshots temporarily

Signed-off-by: tygao <tygao@amazon.com>

* feat: add workspace menu

Signed-off-by: tygao <tygao@amazon.com>

* feat: add menu for table

Signed-off-by: tygao <tygao@amazon.com>

* test: add test for workspace list page

Signed-off-by: tygao <tygao@amazon.com>

* replace lodash and update props name

Signed-off-by: tygao <tygao@amazon.com>

* fix typo

Signed-off-by: tygao <tygao@amazon.com>

* update props name

Signed-off-by: tygao <tygao@amazon.com>

---------

Signed-off-by: tygao <tygao@amazon.com>
  • Loading branch information
raintygao authored Mar 5, 2024
1 parent b14060a commit 0923437
Show file tree
Hide file tree
Showing 12 changed files with 609 additions and 92 deletions.
2 changes: 2 additions & 0 deletions src/core/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,5 @@ export {
WORKSPACE_TYPE,
cleanWorkspaceId,
} from '../utils';

export { debounce } from './utils';
55 changes: 55 additions & 0 deletions src/core/public/utils/debounce.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { debounce } from './debounce';

describe('debounce', () => {
let fn: Function;
beforeEach(() => {
fn = jest.fn();
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
});

test('it should call the debounced fn once at the end of the quiet time', () => {
const debounced = debounce(fn, 1000);

for (let i = 0; i < 100; i++) {
debounced(i);
}

jest.advanceTimersByTime(1001);
expect(fn).toBeCalledTimes(1);
expect(fn).toBeCalledWith(99);
});

test("with a leading invocation, it should call the debounced fn once, if the time doens't pass", () => {
const debounced = debounce(fn, 1000, true);

for (let i = 0; i < 100; i++) {
debounced(i);
}

jest.advanceTimersByTime(999);

expect(fn).toBeCalledTimes(1);
expect(fn).toBeCalledWith(0);
});

test('with a leading invocation, it should call the debounced fn twice (at the beginning and at the end)', () => {
const debounced = debounce(fn, 1000, true);

for (let i = 0; i < 100; i++) {
debounced(i);
}

jest.advanceTimersByTime(1500);

expect(fn).toBeCalledTimes(2);
expect(fn).toBeCalledWith(0);
expect(fn).toBeCalledWith(99);
});
});
23 changes: 23 additions & 0 deletions src/core/public/utils/debounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @param func The function to be debounced.
* @param delay The time in milliseconds to wait before invoking the function again after the last invocation.
* @param leading An optional parameter that, when true, allows the function to be invoked immediately upon the first call.
*/
export const debounce = (func: Function, delay: number, leading?: boolean) => {
let timerId: NodeJS.Timeout;

return (...args: any) => {
if (!timerId && leading) {
func(...args);
}
clearTimeout(timerId);

timerId = setTimeout(() => func(...args), delay);
};
};
1 change: 1 addition & 0 deletions src/core/public/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

export { shareWeakReplay } from './share_weak_replay';
export { Sha256 } from './crypto';
export { debounce } from './debounce';
export { MountWrapper, mountReactNode } from './mount';
export {
WORKSPACE_PATH_PREFIX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,65 @@ import {
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { WorkspaceAttribute } from 'opensearch-dashboards/public';
import { i18n } from '@osd/i18n';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import { WorkspaceClient } from '../../workspace_client';

interface DeleteWorkspaceModalProps {
selectedItems: string[];
onClose: () => void;
onConfirm: () => void;
selectedWorkspace?: WorkspaceAttribute | null;
returnToHome: boolean;
}

export function DeleteWorkspaceModal(props: DeleteWorkspaceModalProps) {
const [value, setValue] = useState('');
const { onClose, onConfirm, selectedItems } = props;
const { onClose, selectedWorkspace, returnToHome } = props;
const {
services: { application, notifications, http, workspaceClient },
} = useOpenSearchDashboards<{ workspaceClient: WorkspaceClient }>();

const deleteWorkspace = async () => {
if (selectedWorkspace?.id) {
let result;
try {
result = await workspaceClient.delete(selectedWorkspace?.id);
} catch (error) {
notifications?.toasts.addDanger({
title: i18n.translate('workspace.delete.failed', {
defaultMessage: 'Failed to delete workspace',
}),
text: error instanceof Error ? error.message : JSON.stringify(error),
});
return onClose();
}
if (result?.success) {
notifications?.toasts.addSuccess({
title: i18n.translate('workspace.delete.success', {
defaultMessage: 'Delete workspace successfully',
}),
});
onClose();
if (http && application && returnToHome) {
const homeUrl = application.getUrlForApp('home', {
path: '/',
absolute: false,
});
const targetUrl = http.basePath.prepend(http.basePath.remove(homeUrl), {
withoutWorkspace: true,
});
await application.navigateToUrl(targetUrl);
}
} else {
notifications?.toasts.addDanger({
title: i18n.translate('workspace.delete.failed', {
defaultMessage: 'Failed to delete workspace',
}),
text: result?.error,
});
}
}
};

return (
<EuiModal onClose={onClose}>
Expand All @@ -37,9 +86,7 @@ export function DeleteWorkspaceModal(props: DeleteWorkspaceModalProps) {
<div style={{ lineHeight: 1.5 }}>
<p>The following workspace will be permanently deleted. This action cannot be undone.</p>
<ul style={{ listStyleType: 'disc', listStylePosition: 'inside' }}>
{selectedItems.map((item) => (
<li key={item}>{item}</li>
))}
{selectedWorkspace?.name ? <li>{selectedWorkspace.name}</li> : null}
</ul>
<EuiSpacer />
<EuiText color="subdued">
Expand All @@ -58,7 +105,7 @@ export function DeleteWorkspaceModal(props: DeleteWorkspaceModalProps) {
<EuiButtonEmpty onClick={onClose}>Cancel</EuiButtonEmpty>
<EuiButton
data-test-subj="Delete Confirm button"
onClick={onConfirm}
onClick={deleteWorkspace}
fill
color="danger"
disabled={value !== 'delete'}
Expand Down
17 changes: 0 additions & 17 deletions src/plugins/workspace/public/components/utils/common.ts

This file was deleted.

Loading

0 comments on commit 0923437

Please sign in to comment.