Skip to content

Commit

Permalink
feat: missing empty state for workspaces dropdown #2305
Browse files Browse the repository at this point in the history
  • Loading branch information
Priyanshu44 authored and diehbria committed Dec 5, 2023
1 parent 9cefd9a commit 11edf2d
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const MAX_QUERY_STATEMENT_LENGTH = 1000;
export const infoMessage = 'To find you assests, make sure to enter the exact model name or characters.';
export const errorMessage = 'Advanced search not supported, no workspace available.';
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { DataStreamSearch } from './dataStreamSearch';
import { IoTTwinMakerClient } from '@aws-sdk/client-iottwinmaker';

jest.mock('./workspaceSelector/useWorkspaces', () => ({
useWorkspaces: jest.fn(() => ({
workspaces: [],
status: 'error',
})),
}));

describe('DataStreamSearch component', () => {
test('renders error and info component when no workspaceis available', async () => {
render(
<QueryClientProvider client={new QueryClient()}>
<DataStreamSearch onSubmit={jest.fn()} client={{ send: jest.fn() } as unknown as IoTTwinMakerClient} />
</QueryClientProvider>
);

const errorMessage = screen.getByText('Advanced search not supported, no workspace available.');
expect(errorMessage).toBeInTheDocument();

const infoMessage = screen.getByText('To find you assests, make sure to enter the exact model name or characters.');
expect(infoMessage).toBeInTheDocument();
});

test('expects to have an external link ', () => {
render(
<QueryClientProvider client={new QueryClient()}>
<DataStreamSearch onSubmit={jest.fn()} client={{ send: jest.fn() } as unknown as IoTTwinMakerClient} />
</QueryClientProvider>
);

expect(screen.getByRole('link', { name: 'Learn more about creating a workspace' })).toHaveAttribute(
'href',
'https://docs.aws.amazon.com/iot-twinmaker/latest/guide/twinmaker-gs-workspace.html'
);
});
});
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
import { type IoTTwinMakerClient } from '@aws-sdk/client-iottwinmaker';
import Button from '@cloudscape-design/components/button';
import Form from '@cloudscape-design/components/form';
import Header from '@cloudscape-design/components/header';
import SpaceBetween from '@cloudscape-design/components/space-between';
import React from 'react';
import { Button, Form, Header, SpaceBetween } from '@cloudscape-design/components';
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';

import { WorkspaceSelector } from './workspaceSelector';
import { SearchQueryInput } from './searchQueryInput';
import type { SearchFields } from './types';
import { WorkspaceErrorState } from './workspaceErrorState';

export interface DataStreamSearchProps {
client: IoTTwinMakerClient;
onSubmit: ({ workspace, searchQuery }: SearchFields) => void;
}

export function DataStreamSearch({ onSubmit, client }: DataStreamSearchProps) {
export const DataStreamSearch = ({ onSubmit, client }: DataStreamSearchProps) => {
const { control, handleSubmit } = useForm<SearchFields>({
defaultValues: { workspace: null, searchQuery: '' },
});

const [isError, setIsError] = useState<boolean>();

return (
<SpaceBetween size='xxs'>
<Header variant='h3' description='Search for modeled data streams by name or by the associated asset.'>
Search modeled data streams
</Header>

{isError && <WorkspaceErrorState />}

<form
onSubmit={(event) => {
event.preventDefault();
Expand All @@ -43,11 +45,11 @@ export function DataStreamSearch({ onSubmit, client }: DataStreamSearchProps) {
}
>
<SpaceBetween size='s'>
<SearchQueryInput control={control} />
<WorkspaceSelector control={control} client={client} />
<SearchQueryInput control={control} disabled={isError} />
<WorkspaceSelector control={control} client={client} OnGettingError={setIsError} />
</SpaceBetween>
</Form>
</form>
</SpaceBetween>
);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { SearchFields } from './types';

export interface SearchQueryInputProps {
control: Control<SearchFields>;
disabled?: boolean;
}

export function SearchQueryInput({ control }: SearchQueryInputProps) {
export const SearchQueryInput = ({ control, disabled }: SearchQueryInputProps) => {
const MIN_SEARCH_QUERY_LENGTH = 1;
const MAX_SEARCH_QUERY_LENGTH = 48;

Expand All @@ -29,6 +30,7 @@ export function SearchQueryInput({ control }: SearchQueryInputProps) {
constraintText={`Must be between ${MIN_SEARCH_QUERY_LENGTH} and ${MAX_SEARCH_QUERY_LENGTH} characters.`}
>
<Input
disabled={disabled}
placeholder='Enter a search query'
value={field.value}
onChange={({ detail: { value } }) => field.onChange(value)}
Expand All @@ -37,4 +39,4 @@ export function SearchQueryInput({ control }: SearchQueryInputProps) {
)}
/>
);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { Alert, Link, SpaceBetween } from '@cloudscape-design/components';
import { infoMessage, errorMessage } from './constants';

export const WorkspaceErrorState = () => {
return (
<SpaceBetween size='s'>
<Alert statusIconAriaLabel='Info' type='info' dismissible dismissAriaLabel='cancel'>
{infoMessage}
</Alert>
<Alert statusIconAriaLabel='Error' type='error' dismissible dismissAriaLabel='cancel'>
{errorMessage}&nbsp;
<Link external href='https://docs.aws.amazon.com/iot-twinmaker/latest/guide/twinmaker-gs-workspace.html'>
Learn more about creating a workspace
</Link>
</Alert>
</SpaceBetween>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type IoTTwinMakerClient } from '@aws-sdk/client-iottwinmaker';
import FormField from '@cloudscape-design/components/form-field';
import Select, { type SelectProps } from '@cloudscape-design/components/select';
import { type useQuery } from '@tanstack/react-query';
import React from 'react';
import React, { useEffect } from 'react';
import { Controller, type Control } from 'react-hook-form';

import { useWorkspaces } from './useWorkspaces';
Expand All @@ -15,12 +15,17 @@ type TanstackStatusType = ReturnType<typeof useQuery>['status'];
export interface WorkspaceSelectorProps {
control: Control<SearchFields>;
client: IoTTwinMakerClient;
OnGettingError: (isError: boolean) => void;
}

export function WorkspaceSelector({ control, client }: WorkspaceSelectorProps) {
export const WorkspaceSelector = ({ control, client, OnGettingError }: WorkspaceSelectorProps) => {
const { workspaces, status } = useWorkspaces({ client });
const workspaceOptions = createWorkspaceOptions(workspaces);

useEffect(() => {
OnGettingError(status === 'error');
}, [status, OnGettingError]);

return (
<Controller
control={control}
Expand All @@ -33,6 +38,7 @@ export function WorkspaceSelector({ control, client }: WorkspaceSelectorProps) {
errorText={fieldState.error?.message}
>
<Select
disabled={status == 'error'}
options={workspaceOptions}
selectedOption={field.value}
onChange={({ detail }) => field.onChange(detail.selectedOption)}
Expand All @@ -45,7 +51,7 @@ export function WorkspaceSelector({ control, client }: WorkspaceSelectorProps) {
)}
/>
);
}
};

function createWorkspaceOptions(workspaces: NonNullable<ReturnType<typeof useWorkspaces>['workspaces']>) {
const workspaceOptionFactory = new WorkspaceOptionFactory();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ export const ResourceExplorerErrorState = ({ title }: { title?: string }) => {
<Box variant='h3'>
<SpaceBetween size='s'>
{title && <label> {title}</label>}
<Alert dismissible statusIconAriaLabel='Error' type='error' header='an error has occurred.' />
<Alert
dismissible
statusIconAriaLabel='Error'
type='error'
header='an error has occurred.'
dismissAriaLabel='cancel'
/>
</SpaceBetween>
</Box>
);
Expand Down

0 comments on commit 11edf2d

Please sign in to comment.