[CORL-406] Tenant Locale Selection (#2450)

* feat: added preload config

* feat: support changing locale

* fix: name case

* fix: removed unused code

* feat: added translations for default reactions

* fix: do not translate icon

* fix: shorter i18n keys
This commit is contained in:
Wyatt Johnson
2019-09-05 17:02:06 +00:00
committed by GitHub
parent 5bf4f22931
commit 04c56b3fb5
54 changed files with 673 additions and 207 deletions
-1
View File
@@ -13,7 +13,6 @@ async function main() {
const ManagedCoralContextProvider = await createManaged({
initLocalState,
localesData,
userLocales: navigator.languages,
});
const Index: FunctionComponent = () => (
@@ -1,6 +1,7 @@
import { FormApi, FormState } from "final-form";
import React from "react";
import { CoralContext, withContext } from "coral-framework/lib/bootstrap";
import { SubmitHookHandler } from "coral-framework/lib/form";
import { MutationProp, withMutation } from "coral-framework/lib/relay";
@@ -9,6 +10,7 @@ import NavigationWarningContainer from "./NavigationWarningContainer";
import UpdateSettingsMutation from "./UpdateSettingsMutation";
interface Props {
changeLocale: CoralContext["changeLocale"];
updateSettings: MutationProp<typeof UpdateSettingsMutation>;
children: React.ReactElement;
}
@@ -27,6 +29,10 @@ class ConfigureRoute extends React.Component<Props, State> {
form: FormApi
) => {
await this.props.updateSettings({ settings: data });
const localeFieldState = form.getFieldState("locale");
if (localeFieldState && localeFieldState.dirty) {
await this.props.changeLocale(data.locale);
}
form.initialize(data);
};
@@ -52,6 +58,8 @@ class ConfigureRoute extends React.Component<Props, State> {
}
}
const enhanced = withMutation(UpdateSettingsMutation)(ConfigureRoute);
const enhanced = withContext(({ changeLocale }) => ({ changeLocale }))(
withMutation(UpdateSettingsMutation)(ConfigureRoute)
);
export default enhanced;
@@ -8,6 +8,7 @@ import ClosingCommentStreamsConfigContainer from "./ClosingCommentStreamsConfigC
import CommentEditingConfigContainer from "./CommentEditingConfigContainer";
import CommentLengthConfigContainer from "./CommentLengthConfigContainer";
import GuidelinesConfigContainer from "./GuidelinesConfigContainer";
import LocaleConfigContainer from "./LocaleConfigContainer";
import ReactionConfigContainer from "./ReactionConfigContainer";
import SitewideCommentingConfigContainer from "./SitewideCommentingConfigContainer";
@@ -19,7 +20,8 @@ interface Props {
PropTypesOf<typeof ClosedStreamMessageConfigContainer>["settings"] &
PropTypesOf<typeof ReactionConfigContainer>["settings"] &
PropTypesOf<typeof ClosingCommentStreamsConfigContainer>["settings"] &
PropTypesOf<typeof SitewideCommentingConfigContainer>["settings"];
PropTypesOf<typeof SitewideCommentingConfigContainer>["settings"] &
PropTypesOf<typeof LocaleConfigContainer>["settings"];
onInitValues: (values: any) => void;
}
@@ -29,6 +31,11 @@ const General: FunctionComponent<Props> = ({
onInitValues,
}) => (
<HorizontalGutter size="double" data-testid="configure-generalContainer">
<LocaleConfigContainer
disabled={disabled}
settings={settings}
onInitValues={onInitValues}
/>
<SitewideCommentingConfigContainer
disabled={disabled}
settings={settings}
@@ -45,6 +45,7 @@ class GeneralConfigContainer extends React.Component<Props> {
const enhanced = withFragmentContainer<Props>({
settings: graphql`
fragment GeneralConfigContainer_settings on Settings {
...LocaleConfigContainer_settings
...GuidelinesConfigContainer_settings
...CommentLengthConfigContainer_settings
...CommentEditingConfigContainer_settings
@@ -0,0 +1,64 @@
import { Localized } from "fluent-react/compat";
import React, { useMemo } from "react";
import { Field } from "react-final-form";
import { graphql } from "react-relay";
import { LocaleConfigContainer_settings } from "coral-admin/__generated__/LocaleConfigContainer_settings.graphql";
import { LocaleField } from "coral-framework/components";
import { withFragmentContainer } from "coral-framework/lib/relay";
import { required } from "coral-framework/lib/validation";
import { FormField, HorizontalGutter, Typography } from "coral-ui/components";
import Header from "../../Header";
import ValidationMessage from "../../ValidationMessage";
interface Props {
settings: LocaleConfigContainer_settings;
onInitValues: (values: LocaleConfigContainer_settings) => void;
disabled: boolean;
}
const LocaleConfigContainer: React.FunctionComponent<Props> = props => {
useMemo(() => props.onInitValues(props.settings), [props.onInitValues]);
return (
<FormField>
<HorizontalGutter size="full">
<Localized id="configure-general-locale-language">
<Header container={<label htmlFor="configure-locale-locale" />}>
Language
</Header>
</Localized>
<Localized
id="configure-general-locale-chooseLanguage"
strong={<strong />}
>
<Typography variant="detail">
Choose the language for your Coral community.
</Typography>
</Localized>
<Field name="locale" validate={required}>
{({ input, meta }) => (
<>
<LocaleField
id={`configure-locale-${input.name}`}
disabled={props.disabled}
{...input}
/>
<ValidationMessage meta={meta} fullWidth />
</>
)}
</Field>
</HorizontalGutter>
</FormField>
);
};
const enhanced = withFragmentContainer<Props>({
settings: graphql`
fragment LocaleConfigContainer_settings on Settings {
locale
}
`,
})(LocaleConfigContainer);
export default enhanced;
@@ -118,6 +118,81 @@ exports[`renders configure general 1`] = `
className="Box-root HorizontalGutter-root HorizontalGutter-double"
data-testid="configure-generalContainer"
>
<div
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<label
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary Header-root"
htmlFor="configure-locale-locale"
>
Language
</label>
<p
className="Box-root Typography-root Typography-detail Typography-colorTextPrimary"
>
Choose the language for your Coral community.
</p>
<span
className="SelectField-root"
>
<select
className="SelectField-select"
disabled={false}
id="configure-locale-locale"
name="locale"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
value="en-US"
>
<option
value="en-US"
>
English
</option>
<option
value="pt-BR"
>
Português brasileiro
</option>
<option
value="es"
>
Español
</option>
<option
value="de"
>
Deutsch
</option>
<option
value="nl-NL"
>
Nederlands
</option>
<option
value="da"
>
Dansk
</option>
</select>
<span
aria-hidden={true}
className="SelectField-afterWrapper"
>
<i
aria-hidden="true"
className="Icon-root Icon-sm"
>
expand_more
</i>
</span>
</span>
</div>
</div>
<fieldset
className="Box-root HorizontalGutter-root HorizontalGutter-oneAndAHalf"
>
@@ -1,3 +1,5 @@
import { SinonStub } from "sinon";
import { ERROR_CODES } from "coral-common/errors";
import { pureMerge } from "coral-common/utils";
import { InvalidRequestError } from "coral-framework/lib/errors";
@@ -63,6 +65,48 @@ it("renders configure general", async () => {
expect(within(configureContainer).toJSON()).toMatchSnapshot();
});
it("change language", async () => {
const resolvers = createResolversStub<GQLResolver>({
Mutation: {
updateSettings: ({ variables }) => {
expectAndFail(variables.settings.locale).toEqual("es");
return {
settings: pureMerge(settings, variables.settings),
};
},
},
});
const {
context: { changeLocale },
configureContainer,
generalContainer,
saveChangesButton,
} = await createTestRenderer({ resolvers });
const languageField = within(generalContainer).getByLabelText("Language");
// Let's change the language.
languageField.props.onChange("es");
// Send form
within(configureContainer)
.getByType("form")
.props.onSubmit();
// Submit button and text field should be disabled.
expect(saveChangesButton.props.disabled).toBe(true);
// Wait for submission to be finished
await wait(() => {
expect(resolvers.Mutation!.updateSettings!.called).toBe(true);
});
// Wait for client to change language.
await wait(() => {
expect((changeLocale as SinonStub).called).toBe(true);
});
});
it("change site wide commenting", async () => {
const resolvers = createResolversStub<GQLResolver>({
Mutation: {
+1
View File
@@ -24,6 +24,7 @@ export const settings = createFixture<GQLSettings>({
id: "settings",
moderation: GQLMODERATION_MODE.POST,
premodLinksEnable: false,
locale: "en-US",
live: {
enabled: true,
configurable: true,