Skip to content

Commit 2beaa44

Browse files
authored
chore: add onSearch callback with loading skeleton (#1373)
1 parent a5aa699 commit 2beaa44

4 files changed

Lines changed: 60 additions & 2 deletions

File tree

‎src/organisms/TopBar/FieldMenu/FieldMenu.stories.tsx‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ function Wrapper(args: FieldMenuProps & { withField?: boolean }) {
3737
onClearField={args.onClear ? handleOnClear : undefined}
3838
showAccessITLink={args.showAccessITLink}
3939
onSelectField={handleOnSelectField}
40+
onSearchChange={args.onSearchChange}
41+
isSearching={args.isSearching}
4042
applicationIcon="acquire"
4143
applicationName="Acquire"
4244
>
@@ -207,3 +209,27 @@ export const OnlyOneField: Story = {
207209
).toBeInTheDocument();
208210
},
209211
};
212+
213+
export const IsSearching: Story = {
214+
args: {
215+
availableFields: [fields[0]],
216+
isSearching: true,
217+
onSearchChange: fn(),
218+
},
219+
play: async ({ canvas, args }) => {
220+
const searchValue = 'missing-field';
221+
const button = canvas.getByTestId('field-selector-top-bar-button');
222+
await userEvent.click(button);
223+
224+
const searchInput = canvas.getByPlaceholderText(/search fields/i);
225+
await userEvent.type(searchInput, searchValue);
226+
227+
await expect(args.onSearchChange).toHaveBeenCalledWith(searchValue);
228+
await expect(
229+
canvas.getByTestId('field-menu-search-skeleton')
230+
).toBeInTheDocument();
231+
await expect(
232+
canvas.queryByText(/no fields matching your search/i)
233+
).not.toBeInTheDocument();
234+
},
235+
};

‎src/organisms/TopBar/FieldMenu/FieldMenu.styles.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Typography } from '@equinor/eds-core-react';
22

33
import { colors, spacings } from 'src/atoms/style';
4+
import { SkeletonBase } from 'src/molecules/Skeleton/SkeletonBase/SkeletonBase';
45

56
import styled, { css } from 'styled-components';
67

@@ -114,3 +115,12 @@ export const TextContainer = styled.div`
114115
text-transform: capitalize;
115116
}
116117
`;
118+
export const ListItemSkeleton = styled(SkeletonBase)`
119+
width: 100%;
120+
height: 24px;
121+
`;
122+
export const MenuItemSkeletonWrapper = styled.div`
123+
border-top: 1px solid ${colors.ui.background__light.rgba};
124+
outline: none !important;
125+
padding: ${spacings.medium} ${spacings.large};
126+
`;

‎src/organisms/TopBar/FieldMenu/FieldMenu.tsx‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import { tokens } from '@equinor/eds-tokens';
1313

1414
import {
1515
ListContainer,
16+
ListItemSkeleton,
1617
MenuFixedItem,
1718
MenuItem,
19+
MenuItemSkeletonWrapper,
1820
MenuSection,
1921
NoFieldsText,
2022
NoSearchResultsContainer,
@@ -34,6 +36,8 @@ export interface FieldMenuProps {
3436
onClear?: () => void;
3537
itemNameSingular?: string; // Defaults to 'field'
3638
showAccessITLink?: boolean;
39+
onSearchChange?: (searchValue: string) => void;
40+
isSearching?: boolean;
3741
}
3842

3943
export const FieldMenu = forwardRef<HTMLDivElement, FieldMenuProps>(
@@ -44,6 +48,8 @@ export const FieldMenu = forwardRef<HTMLDivElement, FieldMenuProps>(
4448
itemNameSingular = 'field',
4549
showAccessITLink = true,
4650
onClear,
51+
onSearchChange,
52+
isSearching = false,
4753
},
4854
ref
4955
) => {
@@ -80,9 +86,14 @@ export const FieldMenu = forwardRef<HTMLDivElement, FieldMenuProps>(
8086
/* v8 ignore next */
8187
const handleSearchOnChange = (event: ChangeEvent<HTMLInputElement>) => {
8288
setSearchValue(event.target.value);
89+
onSearchChange?.(event.target.value);
8390
};
8491

85-
const showSearchInput = availableFields.length >= 4;
92+
const showSearchInput =
93+
availableFields.length >= 4 ||
94+
onSearchChange !== undefined ||
95+
isSearching;
96+
8697
const noSearchResult = filteredFields.length === 0 && showSearchInput;
8798

8899
if (!selectedField && !onClear) return null;
@@ -155,7 +166,7 @@ export const FieldMenu = forwardRef<HTMLDivElement, FieldMenuProps>(
155166
</div>
156167
</MenuFixedItem>
157168
)}
158-
{noSearchResult ? (
169+
{noSearchResult && !isSearching ? (
159170
<NoSearchResultsContainer>
160171
<NoFieldsText
161172
variant="body_short"
@@ -181,6 +192,11 @@ export const FieldMenu = forwardRef<HTMLDivElement, FieldMenuProps>(
181192
</MenuItem>
182193
))
183194
)}
195+
{isSearching && (
196+
<MenuItemSkeletonWrapper>
197+
<ListItemSkeleton data-testid="field-menu-search-skeleton" />
198+
</MenuItemSkeletonWrapper>
199+
)}
184200
</ListContainer>
185201
</MenuSection>
186202
{showAccessITLink && !noSearchResult && (

‎src/organisms/TopBar/TopBar.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export type TopBarBaseProps = {
3131
currentField?: Field;
3232
showAccessITLink?: boolean;
3333
itemNameSingular?: string; // Defaults to 'field'
34+
onSearchChange?: (searchValue: string) => void;
35+
isSearching?: boolean;
3436
} & React.HTMLAttributes<HTMLElement>;
3537

3638
export const TopBar = forwardRef<HTMLDivElement, TopBarBaseProps>(
@@ -50,6 +52,8 @@ export const TopBar = forwardRef<HTMLDivElement, TopBarBaseProps>(
5052
onClearField,
5153
showAccessITLink,
5254
itemNameSingular,
55+
onSearchChange,
56+
isSearching,
5357
},
5458
ref
5559
) => (
@@ -73,6 +77,8 @@ export const TopBar = forwardRef<HTMLDivElement, TopBarBaseProps>(
7377
showAccessITLink={showAccessITLink}
7478
itemNameSingular={itemNameSingular}
7579
onClear={onClearField}
80+
onSearchChange={onSearchChange}
81+
isSearching={isSearching}
7682
/>
7783
)}
7884
{isFetching && <CircularProgress size={16} />}

0 commit comments

Comments
 (0)