Skip to content

Commit a20d2a2

Browse files
authored
feat(sidebar): show loading state when initially loading connections COMPASS-8876 (#6710)
* feat(sidebar): show loading state when initially loading connections * chore(testing-library, sidebar): make preload skip interface more explicit in test helpers; extend the test to check for disabled elements * chore(e2e): update helper commands to account for search bar being on screen when loading connections
1 parent 83ad7e4 commit a20d2a2

File tree

13 files changed

+160
-46
lines changed

13 files changed

+160
-46
lines changed

configs/testing-library-compass/src/index.tsx

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,12 @@ type TestConnectionsOptions = {
8181
*/
8282
preferences?: Partial<AllPreferences>;
8383
/**
84-
* Initial list of connections to be "loaded" to the application
84+
* Initial list of connections to be "loaded" to the application. Empty list
85+
* by default. You can explicitly pass `no-preload` to disable initial
86+
* preloading of the connections, otherwise connections are always preloaded
87+
* before rendering when using helper methods
8588
*/
86-
connections?: ConnectionInfo[];
89+
connections?: ConnectionInfo[] | 'no-preload';
8790
/**
8891
* Connection function that returns DataService when connecting to a
8992
* connection with the connections store. Second argument is a constructor
@@ -245,6 +248,12 @@ const EmptyWrapper = ({ children }: { children: React.ReactElement }) => {
245248
return <>{children}</>;
246249
};
247250

251+
function getConnectionsFromConnectionsOption(
252+
connections: TestConnectionsOptions['connections']
253+
): Exclude<TestConnectionsOptions['connections'], 'no-preload'> {
254+
return connections === 'no-preload' ? undefined : connections ?? [];
255+
}
256+
248257
const TEST_ENV_CURRENT_CONNECTION = {
249258
info: {
250259
id: 'TEST',
@@ -266,6 +275,7 @@ function createWrapper(
266275
TestingLibraryWrapper: ComponentWithChildren = EmptyWrapper,
267276
container?: HTMLElement
268277
) {
278+
const connections = getConnectionsFromConnectionsOption(options.connections);
269279
const wrapperState = {
270280
globalAppRegistry: new AppRegistry(),
271281
localAppRegistry: new AppRegistry(),
@@ -274,7 +284,7 @@ function createWrapper(
274284
logger: createNoopLogger(),
275285
connectionStorage:
276286
options.connectionStorage ??
277-
(new InMemoryConnectionStorage(options.connections) as ConnectionStorage),
287+
(new InMemoryConnectionStorage(connections) as ConnectionStorage),
278288
connectionsStore: {
279289
getState: undefined as unknown as () => State,
280290
actions: {} as ReturnType<typeof useConnectionActions>,
@@ -359,7 +369,7 @@ function createWrapper(
359369
onAutoconnectInfoRequest={
360370
options.onAutoconnectInfoRequest
361371
}
362-
preloadStorageConnectionInfos={options.connections}
372+
preloadStorageConnectionInfos={connections}
363373
>
364374
<StoreGetter>
365375
<TestEnvCurrentConnectionContext.Provider
@@ -416,7 +426,9 @@ function renderWithConnections(
416426
hydrate,
417427
});
418428
expect(
419-
(connectionsOptions.connections ?? []).every((info) => {
429+
(
430+
getConnectionsFromConnectionsOption(connectionsOptions.connections) ?? []
431+
).every((info) => {
420432
return !!wrapperState.connectionsStore.getState().connections.byId[
421433
info.id
422434
];
@@ -510,7 +522,10 @@ async function renderWithActiveConnection(
510522
const renderResult = renderWithConnections(ui, {
511523
...options,
512524
wrapper: ConnectionInfoWrapper,
513-
connections: [connectionInfo, ...(connections ?? [])],
525+
connections: [
526+
connectionInfo,
527+
...(getConnectionsFromConnectionsOption(connections) ?? []),
528+
],
514529
});
515530
await waitForConnect(renderResult.connectionsStore, connectionInfo);
516531
return renderResult;
@@ -532,7 +547,10 @@ async function renderHookWithActiveConnection<HookProps, HookResult>(
532547
const renderHookResult = renderHookWithConnections(cb, {
533548
...options,
534549
wrapper: ConnectionInfoWrapper,
535-
connections: [connectionInfo, ...(connections ?? [])],
550+
connections: [
551+
connectionInfo,
552+
...(getConnectionsFromConnectionsOption(connections) ?? []),
553+
],
536554
});
537555
await waitForConnect(renderHookResult.connectionsStore, connectionInfo);
538556
return renderHookResult;

packages/compass-connections/src/provider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export {
6262
useConnectionsList,
6363
useConnectionsListRef,
6464
connectionsLocator,
65+
useConnectionsListLoadingStatus,
6566
} from './stores/store-context';
6667

6768
export type { ConnectionsService } from './stores/store-context';

packages/compass-connections/src/stores/connections-store-redux.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ describe('CompassConnections store', function () {
6767
.rejects(new Error('loadAll failed'));
6868

6969
renderCompassConnections({
70+
connections: 'no-preload',
7071
connectionStorage,
7172
onFailToLoadConnections: onFailToLoadConnectionsSpy,
7273
});

packages/compass-connections/src/stores/connections-store-redux.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,17 @@ const INITIAL_STATE: State = {
469469
};
470470

471471
export function getInitialConnectionsStateForConnectionInfos(
472-
connectionInfos: ConnectionInfo[] = []
472+
connectionInfos?: ConnectionInfo[]
473473
): State['connections'] {
474+
if (!connectionInfos) {
475+
// Keep initial state if we're not preloading any connections
476+
return {
477+
byId: {},
478+
ids: [],
479+
status: 'initial',
480+
error: null,
481+
};
482+
}
474483
const byId = Object.fromEntries<ConnectionState>(
475484
connectionInfos.map((info) => {
476485
return [info.id, createDefaultConnectionState(info)];
@@ -479,8 +488,7 @@ export function getInitialConnectionsStateForConnectionInfos(
479488
return {
480489
byId,
481490
ids: getSortedIdsForConnections(Object.values(byId)),
482-
// Keep initial state if we're not preloading any connections
483-
status: connectionInfos.length > 0 ? 'ready' : 'initial',
491+
status: 'ready',
484492
error: null,
485493
};
486494
}
@@ -2126,7 +2134,7 @@ export const openSettingsModal = (
21262134
};
21272135

21282136
export function configureStore(
2129-
preloadConnectionInfos: ConnectionInfo[] = [],
2137+
preloadConnectionInfos: ConnectionInfo[] | undefined,
21302138
thunkArg: ThunkExtraArg
21312139
) {
21322140
return createStore(

packages/compass-connections/src/stores/store-context.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,3 +364,14 @@ export function useConnectionsColorList(): {
364364
});
365365
}, isEqual);
366366
}
367+
368+
export function useConnectionsListLoadingStatus() {
369+
return useSelector((state) => {
370+
const status = state.connections.status;
371+
return {
372+
status,
373+
error: state.connections.error?.message ?? null,
374+
isInitialLoad: status === 'initial' || status === 'loading',
375+
};
376+
}, isEqual);
377+
}

packages/compass-e2e-tests/helpers/commands/connect.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,12 @@ export async function waitForConnectionResult(
134134
): Promise<string | undefined> {
135135
const waitOptions = typeof timeout !== 'undefined' ? { timeout } : undefined;
136136

137-
if (await browser.$(Selectors.SidebarFilterInput).isDisplayed()) {
137+
if (
138+
(await browser.$(Selectors.SidebarFilterInput).isDisplayed()) &&
139+
(await browser
140+
.$(Selectors.SidebarFilterInput)
141+
.getAttribute('aria-disabled')) !== 'true'
142+
) {
138143
// Clear the filter to make sure every connection shows
139144
await browser.clickVisible(Selectors.SidebarFilterInput);
140145
await browser.setValueVisible(Selectors.SidebarFilterInput, '');

packages/compass-e2e-tests/helpers/commands/disconnect.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ async function resetForDisconnect(
1515
// and therefore be rendered.
1616
await browser.clickVisible(Selectors.CollapseConnectionsButton);
1717

18-
if (await browser.$(Selectors.SidebarFilterInput).isDisplayed()) {
18+
if (
19+
(await browser.$(Selectors.SidebarFilterInput).isDisplayed()) &&
20+
(await browser
21+
.$(Selectors.SidebarFilterInput)
22+
.getAttribute('aria-disabled')) !== 'true'
23+
) {
1924
// Clear the filter to make sure every connection shows
2025
await browser.clickVisible(Selectors.SidebarFilterInput);
2126
await browser.setValueVisible(Selectors.SidebarFilterInput, '');

packages/compass-e2e-tests/helpers/commands/remove-connections.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ async function resetForRemove(browser: CompassBrowser) {
88
// and therefore be rendered.
99
await browser.clickVisible(Selectors.CollapseConnectionsButton);
1010

11-
if (await browser.$(Selectors.SidebarFilterInput).isDisplayed()) {
11+
if (
12+
(await browser.$(Selectors.SidebarFilterInput).isDisplayed()) &&
13+
(await browser
14+
.$(Selectors.SidebarFilterInput)
15+
.getAttribute('aria-disabled')) !== 'true'
16+
) {
1217
// Clear the filter to make sure every connection shows
1318
await browser.clickVisible(Selectors.SidebarFilterInput);
1419
await browser.setValueVisible(Selectors.SidebarFilterInput, '');

packages/compass-e2e-tests/helpers/commands/sidebar-connection.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,12 @@ export async function removeConnection(
9393
connectionName: string
9494
): Promise<boolean> {
9595
// make sure there's no filter because if the connection is not displayed then we can't remove it
96-
if (await browser.$(Selectors.SidebarFilterInput).isExisting()) {
96+
if (
97+
(await browser.$(Selectors.SidebarFilterInput).isExisting()) &&
98+
(await browser
99+
.$(Selectors.SidebarFilterInput)
100+
.getAttribute('aria-disabled')) !== 'true'
101+
) {
97102
await browser.clickVisible(Selectors.SidebarFilterInput);
98103
await browser.setValueVisible(Selectors.SidebarFilterInput, '');
99104

packages/compass-sidebar/src/components/connections-filter-popover.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ type ConnectionsFilterPopoverProps = PropsWithChildren<{
4343
onFilterChange(
4444
updater: (filter: ConnectionsFilter) => ConnectionsFilter
4545
): void;
46+
disabled?: boolean;
4647
}>;
4748

4849
export default function ConnectionsFilterPopover({
4950
open,
5051
setOpen,
5152
filter,
5253
onFilterChange,
54+
disabled = false,
5355
}: ConnectionsFilterPopoverProps) {
5456
const onExcludeInactiveChange = useCallback(
5557
(excludeInactive: boolean) => {
@@ -103,6 +105,7 @@ export default function ConnectionsFilterPopover({
103105
active={open}
104106
aria-label="Filter connections"
105107
ref={ref}
108+
disabled={disabled}
106109
>
107110
<Icon glyph="Filter" />
108111
{isActivated && (

0 commit comments

Comments
 (0)