forked from airbytehq/airbyte
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🪟 🎉 Add optional invite user hint to connector create pages (airbyteh…
…q#15799) * Add InviteUsersModalService and migrate UserSettingsView to use it * Add InviteUsersHint component and show in create source/destination pages * Update InviteUsersHint to use Text component and spacing variables * Add experiments for showing invite user hint Move cloud settings paths to its own file * Update Invite hint to lazy load with suspense * Fix invite user modal experiment names and add unit test * Rename CloudInviteUsersHint component file * Add notification when users are invited * Fix copy and remove plural form of invite success dialog * Show invite users hint in connector create form page * Fix stylelint issue in InviteUsersHint * Fix access management path in InviteUsersHint * Fix button text in UserSettingsView * Fix linkToUsersPage type in experiments iface * Cleanup code * Cleanup scss path in InviteUsersHint.module * update InviteUsersHint layout to be consistent with or without button
- Loading branch information
1 parent
f4c1d66
commit 0c0fedb
Showing
19 changed files
with
298 additions
and
42 deletions.
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
airbyte-webapp/src/components/CloudInviteUsersHint/CloudInviteUsersHint.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { lazy, Suspense } from "react"; | ||
|
||
import { InviteUsersHintProps } from "packages/cloud/views/users/InviteUsersHint/types"; | ||
import { isCloudApp } from "utils/app"; | ||
|
||
const LazyInviteUsersHint = lazy(() => | ||
import("packages/cloud/views/users/InviteUsersHint").then(({ InviteUsersHint }) => ({ default: InviteUsersHint })) | ||
); | ||
|
||
export const CloudInviteUsersHint: React.VFC<InviteUsersHintProps> = (props) => | ||
isCloudApp() ? ( | ||
<Suspense fallback={null}> | ||
<LazyInviteUsersHint {...props} /> | ||
</Suspense> | ||
) : null; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./CloudInviteUsersHint"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
import NotificationService, { useNotificationService } from "./NotificationService"; | ||
export * from "./types"; | ||
|
||
export default NotificationService; | ||
export { NotificationService, useNotificationService }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
airbyte-webapp/src/packages/cloud/services/users/InviteUsersModalService.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { createContext, useContext, useMemo } from "react"; | ||
import { useToggle } from "react-use"; | ||
|
||
import { InviteUsersModal } from "packages/cloud/views/users/InviteUsersModal"; | ||
|
||
interface InviteUsersModalServiceContext { | ||
isInviteUsersModalOpen: boolean; | ||
toggleInviteUsersModalOpen: (open?: boolean) => void; | ||
} | ||
|
||
const inviteUsersModalServiceContext = createContext<InviteUsersModalServiceContext | null>(null); | ||
const { Provider } = inviteUsersModalServiceContext; | ||
|
||
export const useInviteUsersModalService = () => { | ||
const ctx = useContext(inviteUsersModalServiceContext); | ||
if (!ctx) { | ||
throw new Error("useInviteUsersModalService should be use within InviteUsersModalServiceProvider"); | ||
} | ||
return ctx; | ||
}; | ||
|
||
export const InviteUsersModalServiceProvider: React.FC = ({ children }) => { | ||
const [isOpen, toggleIsOpen] = useToggle(false); | ||
|
||
const contextValue = useMemo<InviteUsersModalServiceContext>( | ||
() => ({ | ||
isInviteUsersModalOpen: isOpen, | ||
toggleInviteUsersModalOpen: toggleIsOpen, | ||
}), | ||
[isOpen, toggleIsOpen] | ||
); | ||
|
||
return ( | ||
<Provider value={contextValue}> | ||
{children} | ||
{isOpen && <InviteUsersModal onClose={toggleIsOpen} />} | ||
</Provider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
airbyte-webapp/src/packages/cloud/views/settings/routePaths.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { SettingsRoute } from "pages/SettingsPage/SettingsPage"; | ||
|
||
export const CloudSettingsRoutes = { | ||
Configuration: SettingsRoute.Configuration, | ||
Notifications: SettingsRoute.Notifications, | ||
Account: SettingsRoute.Account, | ||
Source: SettingsRoute.Source, | ||
Destination: SettingsRoute.Destination, | ||
|
||
Workspace: "workspaces", | ||
AccessManagement: "access-management", | ||
} as const; |
14 changes: 14 additions & 0 deletions
14
airbyte-webapp/src/packages/cloud/views/users/InviteUsersHint/InviteUsersHint.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
@use "scss/variables"; | ||
|
||
.container { | ||
text-align: center; | ||
margin-top: variables.$spacing-lg; | ||
|
||
&.withLink { | ||
padding: variables.$spacing-md; | ||
} | ||
} | ||
|
||
.ctaButton { | ||
margin-left: variables.$spacing-sm; | ||
} |
77 changes: 77 additions & 0 deletions
77
airbyte-webapp/src/packages/cloud/views/users/InviteUsersHint/InviteUsersHint.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { fireEvent, render } from "@testing-library/react"; | ||
import React from "react"; | ||
import { TestWrapper } from "test-utils/testutils"; | ||
|
||
import { Experiments } from "hooks/services/Experiment/experiments"; | ||
import * as ExperimentService from "hooks/services/Experiment/ExperimentService"; | ||
import { RoutePaths } from "pages/routePaths"; | ||
|
||
import { CloudSettingsRoutes } from "../../settings/routePaths"; | ||
|
||
const mockToggleInviteUsersModalOpen = jest.fn(); | ||
jest.doMock("packages/cloud/services/users/InviteUsersModalService", () => ({ | ||
InviteUsersModalServiceProvider: ({ children }: { children: React.ReactNode }): JSX.Element => <>{children}</>, | ||
useInviteUsersModalService: () => ({ | ||
toggleInviteUsersModalOpen: mockToggleInviteUsersModalOpen, | ||
}), | ||
})); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const { InviteUsersHint } = require("./InviteUsersHint"); | ||
|
||
const createUseExperimentMock = | ||
(options: { visible?: boolean; linkToUsersPage?: boolean }) => (key: keyof Experiments) => { | ||
switch (key) { | ||
case "connector.inviteUsersHint.visible": | ||
return options.visible ?? false; | ||
case "connector.inviteUsersHint.linkToUsersPage": | ||
return options.linkToUsersPage ?? false; | ||
default: | ||
throw new Error(`${key} is not mocked`); | ||
} | ||
}; | ||
|
||
describe("InviteUsersHint", () => { | ||
beforeEach(() => { | ||
mockToggleInviteUsersModalOpen.mockReset(); | ||
}); | ||
|
||
it("does not render by default", () => { | ||
const { queryByTestId } = render(<InviteUsersHint connectorType="source" />, { wrapper: TestWrapper }); | ||
expect(queryByTestId("inviteUsersHint")).not.toBeInTheDocument(); | ||
}); | ||
|
||
it("renders when `connector.inviteUserHint.visible` is set to `true`", () => { | ||
jest.spyOn(ExperimentService, "useExperiment").mockImplementation(createUseExperimentMock({ visible: true })); | ||
|
||
const { getByTestId } = render(<InviteUsersHint connectorType="source" />, { wrapper: TestWrapper }); | ||
const element = getByTestId("inviteUsersHint"); | ||
expect(element).toBeInTheDocument(); | ||
}); | ||
|
||
it("opens modal when clicking on CTA by default", () => { | ||
jest.spyOn(ExperimentService, "useExperiment").mockImplementation(createUseExperimentMock({ visible: true })); | ||
|
||
const { getByTestId } = render(<InviteUsersHint connectorType="source" />, { wrapper: TestWrapper }); | ||
const element = getByTestId("inviteUsersHint-cta"); | ||
|
||
expect(element).not.toHaveAttribute("href"); | ||
|
||
fireEvent.click(element); | ||
expect(mockToggleInviteUsersModalOpen).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it("opens link to access-management settings when clicking on CTA and `connector.inviteUsersHint.linkToUsersPage` is `true`", () => { | ||
jest | ||
.spyOn(ExperimentService, "useExperiment") | ||
.mockImplementation(createUseExperimentMock({ visible: true, linkToUsersPage: true })); | ||
|
||
const { getByTestId } = render(<InviteUsersHint connectorType="source" />, { wrapper: TestWrapper }); | ||
const element = getByTestId("inviteUsersHint-cta"); | ||
|
||
expect(element).toHaveAttribute("href", `../${RoutePaths.Settings}/${CloudSettingsRoutes.AccessManagement}`); | ||
|
||
fireEvent.click(element); | ||
expect(mockToggleInviteUsersModalOpen).not.toHaveBeenCalled(); | ||
}); | ||
}); |
67 changes: 67 additions & 0 deletions
67
airbyte-webapp/src/packages/cloud/views/users/InviteUsersHint/InviteUsersHint.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import classNames from "classnames"; | ||
import { FormattedMessage, useIntl } from "react-intl"; | ||
|
||
import { Button } from "components/ui/Button"; | ||
import { Text } from "components/ui/Text"; | ||
|
||
import { useExperiment } from "hooks/services/Experiment"; | ||
import { | ||
InviteUsersModalServiceProvider, | ||
useInviteUsersModalService, | ||
} from "packages/cloud/services/users/InviteUsersModalService"; | ||
import { CloudSettingsRoutes } from "packages/cloud/views/settings/routePaths"; | ||
import { RoutePaths } from "pages/routePaths"; | ||
|
||
import styles from "./InviteUsersHint.module.scss"; | ||
import { InviteUsersHintProps } from "./types"; | ||
|
||
const ACCESS_MANAGEMENT_PATH = `../${RoutePaths.Settings}/${CloudSettingsRoutes.AccessManagement}`; | ||
|
||
const InviteUsersHintContent: React.VFC<InviteUsersHintProps> = ({ connectorType }) => { | ||
const { formatMessage } = useIntl(); | ||
const { toggleInviteUsersModalOpen } = useInviteUsersModalService(); | ||
const linkToUsersPage = useExperiment("connector.inviteUsersHint.linkToUsersPage", false); | ||
|
||
const inviteUsersCta = linkToUsersPage ? ( | ||
<a href={ACCESS_MANAGEMENT_PATH} target="_blank" rel="noreferrer" data-testid="inviteUsersHint-cta"> | ||
<FormattedMessage id="inviteUsersHint.cta" /> | ||
</a> | ||
) : ( | ||
<Button | ||
className={styles.ctaButton} | ||
variant="secondary" | ||
data-testid="inviteUsersHint-cta" | ||
onClick={() => { | ||
toggleInviteUsersModalOpen(); | ||
}} | ||
> | ||
<FormattedMessage id="inviteUsersHint.cta" /> | ||
</Button> | ||
); | ||
|
||
return ( | ||
<Text | ||
size="sm" | ||
className={classNames(styles.container, linkToUsersPage && styles.withLink)} | ||
data-testid="inviteUsersHint" | ||
> | ||
<FormattedMessage | ||
id="inviteUsersHint.message" | ||
values={{ | ||
connector: formatMessage({ id: `connector.${connectorType}` }).toLowerCase(), | ||
}} | ||
/>{" "} | ||
{inviteUsersCta} | ||
</Text> | ||
); | ||
}; | ||
|
||
export const InviteUsersHint: React.VFC<InviteUsersHintProps> = (props) => { | ||
const isVisible = useExperiment("connector.inviteUsersHint.visible", false); | ||
|
||
return isVisible ? ( | ||
<InviteUsersModalServiceProvider> | ||
<InviteUsersHintContent {...props} /> | ||
</InviteUsersModalServiceProvider> | ||
) : null; | ||
}; |
1 change: 1 addition & 0 deletions
1
airbyte-webapp/src/packages/cloud/views/users/InviteUsersHint/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./InviteUsersHint"; |
3 changes: 3 additions & 0 deletions
3
airbyte-webapp/src/packages/cloud/views/users/InviteUsersHint/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export interface InviteUsersHintProps { | ||
connectorType: "source" | "destination"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.