mirror of
https://github.com/wassname/talk.git
synced 2026-08-17 11:27:52 +08:00
[CORL-645] Add Slack support for v5 (#2713)
* Create preliminary schema for setting Slack channels CORL-645 * Implement preliminary slack notification using tenant slack channels CORL-645 * Very preliminarily get data loading with FieldArray's for slack channels CORL-645 * Update settings input on schema to allow saving Slack settings to tenant CORL-645 * Filter off UNMODERATED queue events from slack channels We don't send these to slack through our filters, we only care about pending, reported, and featured CORL-645 * Include a moderation link in comments pushed to Slack CORL-645 * Hook up proper callback functions for adding/removing slack channels CORL-645 * Add missing translation for Slack navigation item CORL-645 * Update snapshots for preliminary Slack configuration changes CORL-645 * Add some FormField wrappers around slack config elements Makes the UI appear a little nicer CORL-645 * Set up slack config to only provide one slack channel We need to do this until we can get ArrayField's working in final-form. CORL-645 * Disable the other trigger checkboxes when "All Comments" is checked CORL-645 * Clean up the formatting of Slack messages CORL-645 * Add error handling around sending comments to Slack CORL-645 * Add links to external Slack setup documentation CORL-645 * Replace form state with wrapped field element CORL-645 * Clean up fetch request sending Slack notifications CORL-645 * Prefer global string replacement over RegEx CORL-645 * Use URL class to construct comment URL's CORL-645 * Require slack configuration in schema CORL-645 * Initialize Slack in fixtures Also fix up a flaky test that wasn't waiting on form submission and on-change events. CORL-645 * Preliminarily fix up styles to match other config pages CORL-645 * Create placeholder add/remove buttons * Convert SlackConfigContainer to FunctionalComponent CORL-645 * Add name field to slack channels CORL-645 * Disable inner fields on Slack channel when not enabled CORL-645 * Improve the delete channel button CORL-645 * Use pureMerge to extract slack channel settings CORL-645 * Do a bit of cleanup on the add channel button CORL-645
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
GeneralConfigRoute,
|
||||
ModerationConfigRoute,
|
||||
OrganizationConfigRoute,
|
||||
SlackConfigRoute,
|
||||
WordListConfigRoute,
|
||||
} from "./routes/Configure/sections";
|
||||
import ForgotPasswordRoute from "./routes/ForgotPassword";
|
||||
@@ -75,6 +76,7 @@ export default makeRouteConfig(
|
||||
<Route path="auth" {...AuthConfigRoute.routeConfig} />
|
||||
<Route path="advanced" {...AdvancedConfigRoute.routeConfig} />
|
||||
<Route path="email" {...EmailConfigRoute.routeConfig} />
|
||||
<Route path="slack" {...SlackConfigRoute.routeConfig} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FormApi, FormState } from "final-form";
|
||||
import arrayMutators from "final-form-arrays";
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Form, FormSpy } from "react-final-form";
|
||||
@@ -24,7 +25,7 @@ const Configure: FunctionComponent<Props> = ({
|
||||
children,
|
||||
}) => (
|
||||
<MainLayout data-testid="configure-container">
|
||||
<Form onSubmit={onSubmit}>
|
||||
<Form onSubmit={onSubmit} mutators={{ ...arrayMutators }}>
|
||||
{({ handleSubmit, submitting, form, pristine, submitError }) => (
|
||||
<form autoComplete="off" onSubmit={handleSubmit} id="configure-form">
|
||||
<FormSpy onChange={onChange} />
|
||||
@@ -52,6 +53,9 @@ const Configure: FunctionComponent<Props> = ({
|
||||
<Localized id="configure-sideBarNavigation-email">
|
||||
<Link to="/admin/configure/email">Email</Link>
|
||||
</Localized>
|
||||
<Localized id="configure-sideBarNavigation-slack">
|
||||
<Link to="/admin/configure/slack">Slack</Link>
|
||||
</Localized>
|
||||
<Localized id="configure-sideBarNavigation-advanced">
|
||||
<Link to="/admin/configure/advanced">Advanced</Link>
|
||||
</Localized>
|
||||
|
||||
@@ -30,6 +30,7 @@ const UpdateSettingsMutation = createMutation(
|
||||
...OrganizationConfigContainer_settings
|
||||
...WordListConfigContainer_settings
|
||||
...AdvancedConfigContainer_settings
|
||||
...SlackConfigContainer_settings
|
||||
}
|
||||
clientMutationId
|
||||
}
|
||||
|
||||
@@ -5,6 +5,19 @@ exports[`renders correctly 1`] = `
|
||||
data-testid="configure-container"
|
||||
>
|
||||
<ReactFinalForm
|
||||
mutators={
|
||||
Object {
|
||||
"insert": [Function],
|
||||
"move": [Function],
|
||||
"pop": [Function],
|
||||
"push": [Function],
|
||||
"remove": [Function],
|
||||
"shift": [Function],
|
||||
"swap": [Function],
|
||||
"unshift": [Function],
|
||||
"update": [Function],
|
||||
}
|
||||
}
|
||||
onSubmit={[Function]}
|
||||
>
|
||||
[Function]
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
.header {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.channelName {
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
.trigger {
|
||||
padding-left: var(--spacing-1);
|
||||
}
|
||||
|
||||
.removeButton {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
|
||||
margin-bottom: 2px;
|
||||
|
||||
float: right;
|
||||
}
|
||||
|
||||
.buttonIcon {
|
||||
padding-right: var(--spacing-1);
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent, useCallback } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
|
||||
import { parseBool } from "coral-framework/lib/form";
|
||||
import { ExternalLink } from "coral-framework/lib/i18n/components";
|
||||
import { InputDescription, InputLabel } from "coral-ui/components";
|
||||
import {
|
||||
Button,
|
||||
ButtonIcon,
|
||||
CheckBox,
|
||||
Flex,
|
||||
FormField,
|
||||
TextField,
|
||||
} from "coral-ui/components/v2";
|
||||
|
||||
import Header from "../../Header";
|
||||
import ConfigBoxWithToggleField from "../Auth/ConfigBoxWithToggleField";
|
||||
|
||||
import styles from "./SlackChannel.css";
|
||||
|
||||
interface Props {
|
||||
channel: any;
|
||||
disabled: boolean;
|
||||
index: number;
|
||||
onRemoveClicked: (index: number) => void;
|
||||
}
|
||||
|
||||
const SlackChannel: FunctionComponent<Props> = ({
|
||||
channel,
|
||||
disabled,
|
||||
index,
|
||||
onRemoveClicked,
|
||||
}) => {
|
||||
const onRemove = useCallback(() => {
|
||||
onRemoveClicked(index);
|
||||
}, [index, onRemoveClicked]);
|
||||
|
||||
return (
|
||||
<ConfigBoxWithToggleField
|
||||
title={
|
||||
<Flex
|
||||
className={styles.header}
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<div>
|
||||
<Field name={`${channel}.name`}>
|
||||
{({ input }) => (
|
||||
<Header className={styles.channelName}>{input.value}</Header>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
size="small"
|
||||
variant="filled"
|
||||
color="alert"
|
||||
onClick={onRemove}
|
||||
className={styles.removeButton}
|
||||
>
|
||||
<ButtonIcon size="md" className={styles.buttonIcon}>
|
||||
delete_forever
|
||||
</ButtonIcon>
|
||||
<Localized id="configure-slack-channel-remove">Remove</Localized>
|
||||
</Button>
|
||||
</div>
|
||||
</Flex>
|
||||
}
|
||||
name={`${channel}.enabled`}
|
||||
disabled={disabled}
|
||||
>
|
||||
{(disabledInside: boolean) => (
|
||||
<>
|
||||
<FormField>
|
||||
<Field name={`${channel}.name`}>
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<Localized id="configure-slack-channel-name-label">
|
||||
<InputLabel container="legend">Name</InputLabel>
|
||||
</Localized>
|
||||
<Localized id="configure-slack-channel-name-description">
|
||||
<InputDescription className={styles.description}>
|
||||
This is only for your information, to easily identify each
|
||||
Slack connection. Slack does not tell us the name of the
|
||||
channel/s you're connecting to Coral.
|
||||
</InputDescription>
|
||||
</Localized>
|
||||
<TextField
|
||||
id={`configure-slack-channel-name-${input.name}`}
|
||||
disabled={disabled || disabledInside}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</FormField>
|
||||
<FormField>
|
||||
<Field name={`${channel}.hookURL`}>
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<Localized id="configure-slack-channel-hookURL-label">
|
||||
<InputLabel container="legend">Webhook URL</InputLabel>
|
||||
</Localized>
|
||||
<Localized
|
||||
id="configure-slack-channel-hookURL-description"
|
||||
externalLink={
|
||||
<ExternalLink href="https://docs.coralproject.net/coral/v5/integrating/slack/#i-need-to-find-the-webhook-url-again-where-is-it" />
|
||||
}
|
||||
>
|
||||
<InputDescription className={styles.description}>
|
||||
Slack provides a channel-specific URL to activate webhook
|
||||
connections. To find the URL for one of your Slack
|
||||
channels, follow the instructions here.
|
||||
</InputDescription>
|
||||
</Localized>
|
||||
<TextField
|
||||
id={`configure-slack-channel-hookURL-${input.name}`}
|
||||
disabled={disabled || disabledInside}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck={false}
|
||||
fullWidth
|
||||
{...input}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</FormField>
|
||||
<FormField>
|
||||
<Localized id="configure-slack-channel-triggers-label">
|
||||
<InputLabel container="legend">
|
||||
Receive notifications in this Slack channel for
|
||||
</InputLabel>
|
||||
</Localized>
|
||||
<Field
|
||||
name={`${channel}.triggers.allComments`}
|
||||
subscription={{ value: true }}
|
||||
>
|
||||
{({ input: { value } }) => (
|
||||
<>
|
||||
<Field
|
||||
name={`${channel}.triggers.allComments`}
|
||||
type="checkbox"
|
||||
parse={parseBool}
|
||||
>
|
||||
{({ input }) => (
|
||||
<CheckBox
|
||||
id={`configure-slack-channel-triggers-allComments-${input.name}`}
|
||||
disabled={disabled || disabledInside}
|
||||
className={styles.trigger}
|
||||
{...input}
|
||||
>
|
||||
<Localized id="configure-slack-channel-triggers-allComments">
|
||||
All Comments
|
||||
</Localized>
|
||||
</CheckBox>
|
||||
)}
|
||||
</Field>
|
||||
<Field
|
||||
name={`${channel}.triggers.reportedComments`}
|
||||
type="checkbox"
|
||||
parse={parseBool}
|
||||
>
|
||||
{({ input }) => (
|
||||
<CheckBox
|
||||
id={`configure-slack-channel-triggers-reportedComments-${input.name}`}
|
||||
disabled={disabled || value || disabledInside}
|
||||
className={styles.trigger}
|
||||
{...input}
|
||||
>
|
||||
<Localized id="configure-slack-channel-triggers-reportedComments">
|
||||
Reported Comments
|
||||
</Localized>
|
||||
</CheckBox>
|
||||
)}
|
||||
</Field>
|
||||
<Field
|
||||
name={`${channel}.triggers.pendingComments`}
|
||||
type="checkbox"
|
||||
parse={parseBool}
|
||||
>
|
||||
{({ input }) => (
|
||||
<CheckBox
|
||||
id={`configure-slack-channel-triggers-pendingComments-${input.name}`}
|
||||
disabled={disabled || value || disabledInside}
|
||||
className={styles.trigger}
|
||||
{...input}
|
||||
>
|
||||
<Localized id="configure-slack-channel-triggers-pendingComments">
|
||||
Pending Comments
|
||||
</Localized>
|
||||
</CheckBox>
|
||||
)}
|
||||
</Field>
|
||||
<Field
|
||||
name={`${channel}.triggers.featuredComments`}
|
||||
type="checkbox"
|
||||
parse={parseBool}
|
||||
>
|
||||
{({ input }) => (
|
||||
<CheckBox
|
||||
id={`configure-slack-channel-triggers-featuredComments-${input.name}`}
|
||||
disabled={disabled || value || disabledInside}
|
||||
className={styles.trigger}
|
||||
{...input}
|
||||
>
|
||||
<Localized id="configure-slack-channel-triggers-featuredComments">
|
||||
Featured Comments
|
||||
</Localized>
|
||||
</CheckBox>
|
||||
)}
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</FormField>
|
||||
</>
|
||||
)}
|
||||
</ConfigBoxWithToggleField>
|
||||
);
|
||||
};
|
||||
|
||||
export default SlackChannel;
|
||||
@@ -0,0 +1,3 @@
|
||||
.icon {
|
||||
padding-right: var(--spacing-2);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
import { FormApi } from "final-form";
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { FieldArray } from "react-final-form-arrays";
|
||||
|
||||
import { pureMerge } from "coral-common/utils";
|
||||
import { ExternalLink } from "coral-framework/lib/i18n/components";
|
||||
import { graphql, withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import {
|
||||
Button,
|
||||
ButtonIcon,
|
||||
FormFieldDescription,
|
||||
HorizontalGutter,
|
||||
} from "coral-ui/components/v2";
|
||||
|
||||
import { SlackConfigContainer_settings } from "coral-admin/__generated__/SlackConfigContainer_settings.graphql";
|
||||
|
||||
import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import SlackChannel from "./SlackChannel";
|
||||
|
||||
import styles from "./SlackConfigContainer.css";
|
||||
|
||||
interface Props {
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
settings: SlackConfigContainer_settings;
|
||||
}
|
||||
|
||||
const SlackConfigContainer: FunctionComponent<Props> = ({ form, settings }) => {
|
||||
const [defaultValues] = useState({
|
||||
slack: {
|
||||
channels: [
|
||||
{
|
||||
enabled: false,
|
||||
name: "",
|
||||
hookURL: "",
|
||||
triggers: {
|
||||
allComments: false,
|
||||
reportedComments: false,
|
||||
pendingComments: false,
|
||||
featuredComments: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const onAddChannel = useCallback(() => {
|
||||
const mutators = form.mutators;
|
||||
mutators.insert("slack.channels", 0, {
|
||||
enabled: true,
|
||||
hookURL: "",
|
||||
triggers: {
|
||||
allComments: false,
|
||||
reportedComments: false,
|
||||
pendingComments: false,
|
||||
featuredComments: false,
|
||||
},
|
||||
});
|
||||
}, [form]);
|
||||
const onRemoveChannel = useCallback(
|
||||
(index: number) => {
|
||||
const mutators = form.mutators;
|
||||
mutators.remove("slack.channels", index);
|
||||
},
|
||||
[form]
|
||||
);
|
||||
|
||||
useMemo(() => {
|
||||
if (
|
||||
!settings ||
|
||||
!settings.slack ||
|
||||
!settings.slack.channels ||
|
||||
settings.slack.channels.length === 0
|
||||
) {
|
||||
form.initialize(defaultValues);
|
||||
} else {
|
||||
const settingsValues = pureMerge(defaultValues, settings);
|
||||
form.initialize(settingsValues);
|
||||
}
|
||||
}, [settings, defaultValues]);
|
||||
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<ConfigBox
|
||||
title={
|
||||
<Localized id="configure-slack-header-title">
|
||||
<Header htmlFor="configure-slack-header.title">
|
||||
Slack Integrations
|
||||
</Header>
|
||||
</Localized>
|
||||
}
|
||||
>
|
||||
<Localized
|
||||
id="configure-slack-description"
|
||||
externalLink={
|
||||
<ExternalLink href="https://docs.coralproject.net/coral/v5/integrating/slack/" />
|
||||
}
|
||||
>
|
||||
<FormFieldDescription>
|
||||
Automatically send comments from Coral moderation queues to Slack
|
||||
channels. You will need Slack admin access to set this up. For steps
|
||||
on how to create a Slack App see our documentation.
|
||||
</FormFieldDescription>
|
||||
</Localized>
|
||||
<Button
|
||||
size="medium"
|
||||
variant="filled"
|
||||
color="emphasis"
|
||||
onClick={onAddChannel}
|
||||
>
|
||||
<ButtonIcon size="md" className={styles.icon}>
|
||||
add
|
||||
</ButtonIcon>
|
||||
<Localized id="configure-slack-addChannel">Add</Localized>
|
||||
</Button>
|
||||
<FieldArray name="slack.channels">
|
||||
{({ fields }) =>
|
||||
fields.map((channel: any, index: number) => (
|
||||
<div key={index}>
|
||||
<SlackChannel
|
||||
channel={channel}
|
||||
disabled={false}
|
||||
index={index}
|
||||
onRemoveClicked={onRemoveChannel}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</FieldArray>
|
||||
</ConfigBox>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
settings: graphql`
|
||||
fragment SlackConfigContainer_settings on Settings {
|
||||
slack {
|
||||
channels {
|
||||
enabled
|
||||
name
|
||||
hookURL
|
||||
triggers {
|
||||
allComments
|
||||
reportedComments
|
||||
pendingComments
|
||||
featuredComments
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(SlackConfigContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -0,0 +1,50 @@
|
||||
import { FormApi } from "final-form";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { SlackConfigRouteQueryResponse } from "coral-admin/__generated__/SlackConfigRouteQuery.graphql";
|
||||
import { withRouteConfig } from "coral-framework/lib/router";
|
||||
import { Delay, Spinner } from "coral-ui/components";
|
||||
|
||||
import SlackConfigContainer from "./SlackConfigContainer";
|
||||
|
||||
interface Props {
|
||||
data: SlackConfigRouteQueryResponse | null;
|
||||
form: FormApi;
|
||||
submitting: boolean;
|
||||
}
|
||||
|
||||
const SlackConfigRoute: FunctionComponent<Props> = ({
|
||||
data,
|
||||
form,
|
||||
submitting,
|
||||
}) => {
|
||||
if (!data) {
|
||||
return (
|
||||
<Delay>
|
||||
<Spinner />
|
||||
</Delay>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SlackConfigContainer
|
||||
settings={data.settings}
|
||||
form={form}
|
||||
submitting={submitting}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withRouteConfig<Props>({
|
||||
query: graphql`
|
||||
query SlackConfigRouteQuery {
|
||||
settings {
|
||||
...SlackConfigContainer_settings
|
||||
}
|
||||
}
|
||||
`,
|
||||
cacheConfig: { force: true },
|
||||
})(SlackConfigRoute);
|
||||
|
||||
export default enhanced;
|
||||
@@ -0,0 +1 @@
|
||||
export { default, default as SlackConfigRoute } from "./SlackConfigRoute";
|
||||
@@ -5,3 +5,4 @@ export { GeneralConfigRoute } from "./General";
|
||||
export { ModerationConfigRoute } from "./Moderation";
|
||||
export { OrganizationConfigRoute } from "./Organization";
|
||||
export { WordListConfigRoute } from "./WordList";
|
||||
export { SlackConfigRoute } from "./Slack";
|
||||
|
||||
@@ -79,6 +79,15 @@ exports[`renders configure advanced 1`] = `
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
href="/admin/configure/slack"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Slack
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link Link-linkActive"
|
||||
|
||||
@@ -79,6 +79,15 @@ exports[`renders configure auth 1`] = `
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
href="/admin/configure/slack"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Slack
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
|
||||
@@ -79,6 +79,15 @@ exports[`renders configure general 1`] = `
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
href="/admin/configure/slack"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Slack
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
|
||||
@@ -79,6 +79,15 @@ exports[`renders configure moderation 1`] = `
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
href="/admin/configure/slack"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Slack
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
|
||||
@@ -79,6 +79,15 @@ exports[`renders configure organization 1`] = `
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
href="/admin/configure/slack"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Slack
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
|
||||
@@ -79,6 +79,15 @@ exports[`renders configure wordList 1`] = `
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
href="/admin/configure/slack"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Slack
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="Link-link"
|
||||
|
||||
@@ -90,14 +90,16 @@ it("change language", async () => {
|
||||
act(() => languageField.props.onChange("es"));
|
||||
|
||||
// Send form
|
||||
act(() => {
|
||||
within(configureContainer)
|
||||
await act(async () => {
|
||||
await within(configureContainer)
|
||||
.getByType("form")
|
||||
.props.onSubmit();
|
||||
});
|
||||
|
||||
// Submit button and text field should be disabled.
|
||||
expect(saveChangesButton.props.disabled).toBe(true);
|
||||
await wait(() => {
|
||||
expect(saveChangesButton.props.disabled).toBe(true);
|
||||
});
|
||||
|
||||
// Wait for submission to be finished
|
||||
await act(async () => {
|
||||
|
||||
@@ -161,6 +161,9 @@ export const settings = createFixture<GQLSettings>({
|
||||
changeUsername: true,
|
||||
deleteAccount: true,
|
||||
},
|
||||
slack: {
|
||||
channels: [],
|
||||
},
|
||||
});
|
||||
|
||||
export const settingsWithEmptyAuth = createFixture<GQLSettings>(
|
||||
|
||||
Reference in New Issue
Block a user