Skip to content

Commit

Permalink
Add loading indicator and other polish
Browse files Browse the repository at this point in the history
  • Loading branch information
phillipb committed Jun 26, 2020
1 parent c6d3784 commit 1b677ed
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface Props<ViewState> {
views: Array<SavedView<ViewState>>;
loading: boolean;
defaultViewId: string;
sourceIsLoading: boolean;
close(): void;
makeDefault(id: string): void;
setView(viewState: ViewState): void;
Expand Down Expand Up @@ -76,7 +77,9 @@ export function SavedViewManageViewsFlyout<ViewState>({
makeDefault,
deleteView,
loading,
sourceIsLoading,
}: Props<ViewState>) {
const [inProgressView, setInProgressView] = useState<string | null>(null);
const renderName = useCallback(
(name: string, item: SavedView<ViewState>) => (
<EuiButtonEmpty
Expand Down Expand Up @@ -111,13 +114,17 @@ export function SavedViewManageViewsFlyout<ViewState>({
return (
<>
<EuiButtonEmpty
isLoading={inProgressView === item.id && sourceIsLoading}
iconType={isDefault ? 'starFilled' : 'starEmpty'}
onClick={() => makeDefault(item.id)}
onClick={() => {
setInProgressView(item.id);
makeDefault(item.id);
}}
/>
</>
);
},
[makeDefault, defaultViewId]
[makeDefault, defaultViewId, sourceIsLoading, inProgressView]
);

const columns = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ interface Props<ViewState> {
viewState: ViewState;
}

export function SavedViewsToolbarControls<ViewState>(props: Props<ViewState>) {
export function SavedViewsToolbarControls<ViewState extends { id: string; name: string }>(
props: Props<ViewState>
) {
const kibana = useKibana();
const {
views,
Expand All @@ -39,6 +41,7 @@ export function SavedViewsToolbarControls<ViewState>(props: Props<ViewState>) {
deleteView,
defaultViewId,
makeDefault,
sourceIsLoading,
find,
errorOnFind,
errorOnCreate,
Expand Down Expand Up @@ -220,11 +223,17 @@ export function SavedViewsToolbarControls<ViewState>(props: Props<ViewState>) {
)}

{updateModalOpen && (
<SavedViewUpdateModal isInvalid={isInvalid} close={closeUpdateModal} save={update} />
<SavedViewUpdateModal
currentView={currentView}
isInvalid={isInvalid}
close={closeUpdateModal}
save={update}
/>
)}

{viewListModalOpen && (
<SavedViewListModal<ViewState>
currentView={currentView}
views={views}
close={closeViewListModal}
setView={setCurrentView}
Expand All @@ -233,6 +242,7 @@ export function SavedViewsToolbarControls<ViewState>(props: Props<ViewState>) {

{modalOpen && (
<SavedViewManageViewsFlyout<ViewState>
sourceIsLoading={sourceIsLoading}
loading={loading}
views={views}
defaultViewId={defaultViewId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@ import {
EuiText,
} from '@elastic/eui';

interface Props {
interface Props<ViewState> {
isInvalid: boolean;
close(): void;
save(name: string, shouldIncludeTime: boolean): void;
currentView: ViewState;
}

export const SavedViewUpdateModal = ({ close, save, isInvalid }: Props) => {
const [viewName, setViewName] = useState('');
export function SavedViewUpdateModal<ViewState extends { id: string; name: string }>({
close,
save,
isInvalid,
currentView,
}: Props<ViewState>) {
const [viewName, setViewName] = useState(currentView.name);
const [includeTime, setIncludeTime] = useState(false);
const onCheckChange = useCallback((e) => setIncludeTime(e.target.checked), []);
const textChange = useCallback((e) => setViewName(e.target.value), []);
Expand Down Expand Up @@ -104,4 +110,4 @@ export const SavedViewUpdateModal = ({ close, save, isInvalid }: Props) => {
</EuiModal>
</EuiOverlayMask>
);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useCallback, useState } from 'react';
import React, { useCallback, useState, useMemo } from 'react';

import { EuiButtonEmpty, EuiModalFooter, EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
Expand All @@ -24,9 +24,15 @@ interface Props<ViewState> {
views: Array<SavedView<ViewState>>;
close(): void;
setView(viewState: ViewState): void;
currentView?: ViewState;
}

export function SavedViewListModal<ViewState>({ close, views, setView }: Props<ViewState>) {
export function SavedViewListModal<ViewState extends { id: string; name: string }>({
close,
views,
setView,
currentView,
}: Props<ViewState>) {
const [options, setOptions] = useState<EuiSelectableOption[] | null>(null);

const onChange = useCallback((opts: EuiSelectableOption[]) => {
Expand All @@ -48,6 +54,14 @@ export function SavedViewListModal<ViewState>({ close, views, setView }: Props<V
close();
}, [options, views, setView, close]);

const defaultOptions = useMemo<EuiSelectableOption[]>(() => {
return views.map((v) => ({
label: v.name,
key: v.id,
checked: currentView?.id === v.id ? 'on' : undefined,
}));
}, [views, currentView]); // eslint-disable-line react-hooks/exhaustive-deps

return (
<EuiOverlayMask>
<EuiModal onClose={close}>
Expand All @@ -63,7 +77,7 @@ export function SavedViewListModal<ViewState>({ close, views, setView }: Props<V
<EuiSelectable
singleSelection={true}
searchable={true}
options={options || views.map((v) => ({ label: v.name, key: v.id }))}
options={options || defaultOptions}
onChange={onChange}
searchProps={{
placeholder: i18n.translate('xpack.infra.savedView.searchPlaceholder', {
Expand Down
15 changes: 10 additions & 5 deletions x-pack/plugins/infra/public/containers/saved_view/saved_view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ interface Props {
}

export const useSavedView = (props: Props) => {
const { source, sourceExists, createSourceConfiguration, updateSourceConfiguration } = useContext(
Source.Context
);
const {
source,
isLoading: sourceIsLoading,
sourceExists,
createSourceConfiguration,
updateSourceConfiguration,
} = useContext(Source.Context);
const { viewType, defaultViewState } = props;
type ViewState = typeof defaultViewState;
const { data, loading, find, error: errorOnFind, hasView } = useFindSavedObject<
Expand Down Expand Up @@ -115,8 +119,8 @@ export const useSavedView = (props: Props) => {
const updateView = useCallback(
(id, d: { [p: string]: any }) => {
const doSave = async () => {
const exists = await hasView(d.name);
if (exists) {
const view = await hasView(d.name);
if (view && view.id !== id) {
setCreateError(
i18n.translate('xpack.infra.savedView.errorOnCreate.duplicateViewName', {
defaultMessage: `A view with that name already exists.`,
Expand Down Expand Up @@ -244,6 +248,7 @@ export const useSavedView = (props: Props) => {
errorOnCreate: createError,
shouldLoadDefault: props.shouldLoadDefault,
makeDefault,
sourceIsLoading,
deleteView,
loadingDefaultView,
setCurrentView,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const useFindSavedObject = <SavedObjectType extends SavedObjectAttributes
const objects = await savedObjectsClient.find<SavedObjectType>({
type,
});
return objects.savedObjects.filter((o) => o.attributes.name === name).length > 0;
return objects.savedObjects.find((o) => o.attributes.name === name);
};

return {
Expand Down

0 comments on commit 1b677ed

Please sign in to comment.