mirror of
https://github.com/wassname/talk.git
synced 2026-06-27 19:17:09 +08:00
[CORL-761] stream-side account tab for sso (#2834)
* move download comments to my comments tab * only show download comments if available * move ignored users management to same tab as notifications, rename to preferences * fix query and ts defs * add url to jwt * make account tab go to external url if provided * ensure url is an optional jwt field * update tabs for stream profile * update classnames for tabs * fix tests
This commit is contained in:
@@ -106,6 +106,12 @@ const CLASSES = {
|
||||
myComments:
|
||||
"coral coral-tabBarSecondary-tab coral-tabBarMyProfile-myComments",
|
||||
|
||||
/**
|
||||
* preferences is the button for the "Preferences" tab.
|
||||
*/
|
||||
preferences:
|
||||
"coral coral-tabBarSecondary-tab coral-tabBarMyProfile-preferences",
|
||||
|
||||
/**
|
||||
* notifications is the button for the "Notifications" tab.
|
||||
*/
|
||||
@@ -744,6 +750,10 @@ const CLASSES = {
|
||||
$root: "coral coral-notifications",
|
||||
},
|
||||
|
||||
preferencesTabPane: {
|
||||
$root: "coral coral-preferences",
|
||||
},
|
||||
|
||||
/**
|
||||
* accountTabPane is the tab pane that shows account settings.
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,7 @@ enum TAB {
|
||||
enum PROFILE_TAB {
|
||||
MY_COMMENTS
|
||||
ACCOUNT
|
||||
NOTIFICATIONS
|
||||
PREFERENCES
|
||||
}
|
||||
|
||||
enum COMMENTS_TAB {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export {
|
||||
default,
|
||||
default as CommentHistoryContainer,
|
||||
} from "./CommentHistoryContainer";
|
||||
+1
-1
@@ -13,7 +13,7 @@ import { Button, CallOut, Flex, Icon, Typography } from "coral-ui/components";
|
||||
|
||||
import { DownloadCommentsContainer_viewer } from "coral-stream/__generated__/DownloadCommentsContainer_viewer.graphql";
|
||||
|
||||
import RequestCommentsDownloadMutation from "./RequestCommentsDownloadMutation";
|
||||
import RequestCommentsDownloadMutation from "../Settings/RequestCommentsDownloadMutation";
|
||||
|
||||
import styles from "./DownloadCommentsContainer.css";
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
|
||||
import { MyCommentsContainer_settings } from "coral-stream/__generated__/MyCommentsContainer_settings.graphql";
|
||||
import { MyCommentsContainer_story } from "coral-stream/__generated__/MyCommentsContainer_story.graphql";
|
||||
import { MyCommentsContainer_viewer } from "coral-stream/__generated__/MyCommentsContainer_viewer.graphql";
|
||||
|
||||
import CommentHistoryContainer from "./CommentHistoryContainer";
|
||||
import DownloadCommentsContainer from "./DownloadCommentsContainer";
|
||||
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
// import styles from "./MyComments.css";
|
||||
|
||||
interface Props {
|
||||
settings: MyCommentsContainer_settings;
|
||||
viewer: MyCommentsContainer_viewer;
|
||||
story: MyCommentsContainer_story;
|
||||
}
|
||||
|
||||
const MyCommentsContainer: FunctionComponent<Props> = ({
|
||||
settings,
|
||||
viewer,
|
||||
story,
|
||||
}) => {
|
||||
return (
|
||||
<HorizontalGutter spacing={6}>
|
||||
<CommentHistoryContainer
|
||||
settings={settings}
|
||||
viewer={viewer}
|
||||
story={story}
|
||||
/>
|
||||
{settings.accountFeatures.downloadComments && (
|
||||
<DownloadCommentsContainer viewer={viewer} />
|
||||
)}
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
viewer: graphql`
|
||||
fragment MyCommentsContainer_viewer on User {
|
||||
...CommentHistoryContainer_viewer
|
||||
...DownloadCommentsContainer_viewer
|
||||
}
|
||||
`,
|
||||
story: graphql`
|
||||
fragment MyCommentsContainer_story on Story {
|
||||
...CommentHistoryContainer_story
|
||||
}
|
||||
`,
|
||||
settings: graphql`
|
||||
fragment MyCommentsContainer_settings on Settings {
|
||||
accountFeatures {
|
||||
downloadComments
|
||||
}
|
||||
...CommentHistoryContainer_settings
|
||||
}
|
||||
`,
|
||||
})(MyCommentsContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -0,0 +1 @@
|
||||
export { default, default as MyCommentsContainer } from "./MyCommentsContainer";
|
||||
+1
-1
@@ -16,8 +16,8 @@ import {
|
||||
|
||||
import { IgnoreUserSettingsContainer_viewer as ViewerData } from "coral-stream/__generated__/IgnoreUserSettingsContainer_viewer.graphql";
|
||||
|
||||
import Username from "../Settings/Username";
|
||||
import RemoveUserIgnoreMutation from "./RemoveUserIgnoreMutation";
|
||||
import Username from "./Username";
|
||||
|
||||
import styles from "./IgnoreUserSettingsContainer.css";
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { graphql, withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
|
||||
import IgnoreUserSettingsContainer from "./IgnoreUserSettingsContainer";
|
||||
import NotificationSettingsContainer from "./NotificationSettingsContainer";
|
||||
|
||||
import { PreferencesContainer_viewer } from "coral-stream/__generated__/PreferencesContainer_viewer.graphql";
|
||||
|
||||
interface Props {
|
||||
viewer: PreferencesContainer_viewer;
|
||||
}
|
||||
|
||||
const PreferencesContainer: FunctionComponent<Props> = props => {
|
||||
return (
|
||||
<HorizontalGutter spacing={4}>
|
||||
<NotificationSettingsContainer viewer={props.viewer} />
|
||||
<IgnoreUserSettingsContainer viewer={props.viewer} />
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
viewer: graphql`
|
||||
fragment PreferencesContainer_viewer on User {
|
||||
...NotificationSettingsContainer_viewer
|
||||
...IgnoreUserSettingsContainer_viewer
|
||||
}
|
||||
`,
|
||||
})(PreferencesContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -0,0 +1,4 @@
|
||||
export {
|
||||
default,
|
||||
default as PreferencesContainer,
|
||||
} from "./PreferencesContainer";
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent, useCallback } from "react";
|
||||
import React, { FunctionComponent, useCallback, useMemo } from "react";
|
||||
|
||||
import { useViewerEvent } from "coral-framework/lib/events";
|
||||
import { graphql, useLocal } from "coral-framework/lib/relay";
|
||||
@@ -17,23 +17,23 @@ import {
|
||||
|
||||
import { ProfileLocal } from "coral-stream/__generated__/ProfileLocal.graphql";
|
||||
|
||||
import CommentHistoryContainer from "./CommentHistory";
|
||||
import DeletionRequestCalloutContainer from "./DeletionRequest/DeletionRequestCalloutContainer";
|
||||
import MyCommentsContainer from "./MyComments";
|
||||
import PreferencesContainer from "./Preferences";
|
||||
import AccountSettingsContainer from "./Settings";
|
||||
import NotificationSettingsContainer from "./Settings/NotificationSettingsContainer";
|
||||
|
||||
export interface ProfileProps {
|
||||
story: PropTypesOf<typeof CommentHistoryContainer>["story"];
|
||||
isSSO: boolean;
|
||||
ssoURL: string | null;
|
||||
story: PropTypesOf<typeof MyCommentsContainer>["story"];
|
||||
viewer: PropTypesOf<typeof UserBoxContainer>["viewer"] &
|
||||
PropTypesOf<typeof CommentHistoryContainer>["viewer"] &
|
||||
PropTypesOf<typeof AccountSettingsContainer>["viewer"] &
|
||||
PropTypesOf<typeof MyCommentsContainer>["viewer"] &
|
||||
PropTypesOf<typeof AccountSettingsContainer>["viewer"] &
|
||||
PropTypesOf<typeof DeletionRequestCalloutContainer>["viewer"] &
|
||||
PropTypesOf<typeof AccountSettingsContainer>["viewer"] &
|
||||
PropTypesOf<typeof NotificationSettingsContainer>["viewer"];
|
||||
PropTypesOf<typeof PreferencesContainer>["viewer"];
|
||||
settings: PropTypesOf<typeof UserBoxContainer>["settings"] &
|
||||
PropTypesOf<typeof AccountSettingsContainer>["settings"] &
|
||||
PropTypesOf<typeof CommentHistoryContainer>["settings"];
|
||||
PropTypesOf<typeof MyCommentsContainer>["settings"];
|
||||
}
|
||||
|
||||
const Profile: FunctionComponent<ProfileProps> = props => {
|
||||
@@ -45,13 +45,31 @@ const Profile: FunctionComponent<ProfileProps> = props => {
|
||||
`);
|
||||
const onTabClick = useCallback(
|
||||
(tab: ProfileLocal["profileTab"]) => {
|
||||
if (local.profileTab !== tab) {
|
||||
if (
|
||||
tab === "ACCOUNT" &&
|
||||
props.isSSO &&
|
||||
props.ssoURL &&
|
||||
props.ssoURL.length > 0
|
||||
) {
|
||||
window.open(props.ssoURL);
|
||||
} else if (local.profileTab !== tab) {
|
||||
emitSetProfileTabEvent({ tab });
|
||||
setLocal({ profileTab: tab });
|
||||
}
|
||||
},
|
||||
[setLocal, local.profileTab]
|
||||
[setLocal, local.profileTab, props.ssoURL, props.isSSO]
|
||||
);
|
||||
|
||||
const showAccountTab = useMemo(() => {
|
||||
if (!props.isSSO) {
|
||||
return true;
|
||||
}
|
||||
if (props.ssoURL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, [props.ssoURL, props.isSSO]);
|
||||
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<UserBoxContainer viewer={props.viewer} settings={props.settings} />
|
||||
@@ -67,43 +85,47 @@ const Profile: FunctionComponent<ProfileProps> = props => {
|
||||
</Localized>
|
||||
</Tab>
|
||||
<Tab
|
||||
tabID="NOTIFICATIONS"
|
||||
className={CLASSES.tabBarMyProfile.notifications}
|
||||
tabID="PREFERENCES"
|
||||
className={CLASSES.tabBarMyProfile.preferences}
|
||||
>
|
||||
<Localized id="profile-notificationsTab">
|
||||
<span>Notifications</span>
|
||||
</Localized>
|
||||
</Tab>
|
||||
<Tab tabID="ACCOUNT" className={CLASSES.tabBarMyProfile.settings}>
|
||||
<Localized id="profile-accountTab">
|
||||
<span>Account</span>
|
||||
<Localized id="profile-preferencesTab">
|
||||
<span>Preferences</span>
|
||||
</Localized>
|
||||
</Tab>
|
||||
{showAccountTab && (
|
||||
<Tab tabID="ACCOUNT" className={CLASSES.tabBarMyProfile.settings}>
|
||||
<Localized id="profile-accountTab">
|
||||
<span>Account</span>
|
||||
</Localized>
|
||||
</Tab>
|
||||
)}
|
||||
</TabBar>
|
||||
<TabContent activeTab={local.profileTab}>
|
||||
<TabPane
|
||||
className={CLASSES.myCommentsTabPane.$root}
|
||||
tabID="MY_COMMENTS"
|
||||
>
|
||||
<CommentHistoryContainer
|
||||
<MyCommentsContainer
|
||||
settings={props.settings}
|
||||
viewer={props.viewer}
|
||||
story={props.story}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
className={CLASSES.notificationsTabPane.$root}
|
||||
tabID="NOTIFICATIONS"
|
||||
className={CLASSES.preferencesTabPane.$root}
|
||||
tabID="PREFERENCES"
|
||||
>
|
||||
<NotificationSettingsContainer viewer={props.viewer} />
|
||||
</TabPane>
|
||||
<TabPane className={CLASSES.accountTabPane.$root} tabID="ACCOUNT">
|
||||
<AccountSettingsContainer
|
||||
viewer={props.viewer}
|
||||
settings={props.settings}
|
||||
/>
|
||||
<DeletionRequestCalloutContainer viewer={props.viewer} />
|
||||
<PreferencesContainer viewer={props.viewer} />
|
||||
</TabPane>
|
||||
{showAccountTab && (
|
||||
<TabPane className={CLASSES.accountTabPane.$root} tabID="ACCOUNT">
|
||||
<AccountSettingsContainer
|
||||
viewer={props.viewer}
|
||||
settings={props.settings}
|
||||
/>
|
||||
<DeletionRequestCalloutContainer viewer={props.viewer} />
|
||||
</TabPane>
|
||||
)}
|
||||
</TabContent>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isUndefined } from "lodash";
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
@@ -17,11 +18,16 @@ interface ProfileContainerProps {
|
||||
|
||||
export class ProfileContainer extends React.Component<ProfileContainerProps> {
|
||||
public render() {
|
||||
const ssoProfile = this.props.viewer.profiles.find(
|
||||
profile => profile.__typename === "SSOProfile"
|
||||
);
|
||||
return (
|
||||
<Profile
|
||||
viewer={this.props.viewer}
|
||||
story={this.props.story}
|
||||
settings={this.props.settings}
|
||||
isSSO={!isUndefined(ssoProfile)}
|
||||
ssoURL={this.props.viewer.ssoURL}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -29,25 +35,27 @@ export class ProfileContainer extends React.Component<ProfileContainerProps> {
|
||||
const enhanced = withFragmentContainer<ProfileContainerProps>({
|
||||
story: graphql`
|
||||
fragment ProfileContainer_story on Story {
|
||||
...CommentHistoryContainer_story
|
||||
...MyCommentsContainer_story
|
||||
}
|
||||
`,
|
||||
viewer: graphql`
|
||||
fragment ProfileContainer_viewer on User {
|
||||
...UserBoxContainer_viewer
|
||||
...CommentHistoryContainer_viewer
|
||||
...AccountSettingsContainer_viewer
|
||||
...ChangeUsernameContainer_viewer
|
||||
...ChangeEmailContainer_viewer
|
||||
...MyCommentsContainer_viewer
|
||||
...DeletionRequestCalloutContainer_viewer
|
||||
...NotificationSettingsContainer_viewer
|
||||
...PreferencesContainer_viewer
|
||||
profiles {
|
||||
__typename
|
||||
}
|
||||
ssoURL
|
||||
}
|
||||
`,
|
||||
settings: graphql`
|
||||
fragment ProfileContainer_settings on Settings {
|
||||
...UserBoxContainer_settings
|
||||
...AccountSettingsContainer_settings
|
||||
...CommentHistoryContainer_settings
|
||||
...MyCommentsContainer_settings
|
||||
}
|
||||
`,
|
||||
})(ProfileContainer);
|
||||
|
||||
@@ -12,8 +12,7 @@ import ChangeEmailContainer from "./ChangeEmail";
|
||||
import ChangePasswordContainer from "./ChangePasswordContainer";
|
||||
import ChangeUsernameContainer from "./ChangeUsername";
|
||||
import DeleteAccountContainer from "./DeleteAccount/DeleteAccountContainer";
|
||||
import DownloadCommentsContainer from "./DownloadCommentsContainer";
|
||||
import IgnoreUserSettingsContainer from "./IgnoreUserSettingsContainer";
|
||||
// import DownloadCommentsContainer from "../CommentHistory/DownloadCommentsContainer";
|
||||
|
||||
import styles from "./AccountSettingsContainer.css";
|
||||
|
||||
@@ -26,7 +25,7 @@ const AccountSettingsContainer: FunctionComponent<Props> = ({
|
||||
viewer,
|
||||
settings,
|
||||
}) => (
|
||||
<HorizontalGutter size="oneAndAHalf">
|
||||
<HorizontalGutter size="oneAndAHalf" data-testid="profile-manageAccount">
|
||||
<Localized id="accountSettings-manage-account">
|
||||
<Typography variant="heading1">Manage your account</Typography>
|
||||
</Localized>
|
||||
@@ -34,10 +33,6 @@ const AccountSettingsContainer: FunctionComponent<Props> = ({
|
||||
<ChangeUsernameContainer settings={settings} viewer={viewer} />
|
||||
<ChangeEmailContainer settings={settings} viewer={viewer} />
|
||||
<ChangePasswordContainer settings={settings} />
|
||||
<IgnoreUserSettingsContainer viewer={viewer} />
|
||||
{settings.accountFeatures.downloadComments && (
|
||||
<DownloadCommentsContainer viewer={viewer} />
|
||||
)}
|
||||
{settings.accountFeatures.deleteAccount && (
|
||||
<DeleteAccountContainer viewer={viewer} settings={settings} />
|
||||
)}
|
||||
@@ -48,25 +43,20 @@ const AccountSettingsContainer: FunctionComponent<Props> = ({
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
viewer: graphql`
|
||||
fragment AccountSettingsContainer_viewer on User {
|
||||
...IgnoreUserSettingsContainer_viewer
|
||||
...DownloadCommentsContainer_viewer
|
||||
...DeleteAccountContainer_viewer
|
||||
...ChangeUsernameContainer_viewer
|
||||
...ChangeEmailContainer_viewer
|
||||
...UserBoxContainer_viewer
|
||||
}
|
||||
`,
|
||||
settings: graphql`
|
||||
fragment AccountSettingsContainer_settings on Settings {
|
||||
accountFeatures {
|
||||
downloadComments
|
||||
deleteAccount
|
||||
}
|
||||
...ChangePasswordContainer_settings
|
||||
...DeleteAccountContainer_settings
|
||||
...ChangeEmailContainer_settings
|
||||
...ChangeUsernameContainer_settings
|
||||
...UserBoxContainer_settings
|
||||
}
|
||||
`,
|
||||
})(AccountSettingsContainer);
|
||||
|
||||
@@ -2,102 +2,128 @@
|
||||
|
||||
exports[`renders the empty settings pane 1`] = `
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root coral coral-stream App-root HorizontalGutter-full"
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-oneAndAHalf"
|
||||
data-testid="profile-manageAccount"
|
||||
>
|
||||
<nav>
|
||||
<h2
|
||||
className="AriaInfo-root"
|
||||
>
|
||||
Navigation
|
||||
</h2>
|
||||
<ul
|
||||
className="TabBar-root TabBar-primary coral coral-tabBar"
|
||||
role="tablist"
|
||||
>
|
||||
<li
|
||||
className="Tab-root"
|
||||
id="tab-COMMENTS"
|
||||
role="presentation"
|
||||
>
|
||||
<button
|
||||
aria-controls="tabPane-COMMENTS"
|
||||
aria-selected={false}
|
||||
className="BaseButton-root Tab-button Tab-primary coral coral-tabBar-tab coral-tabBar-comments"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
role="tab"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Comments
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
className="Tab-root"
|
||||
id="tab-PROFILE"
|
||||
role="presentation"
|
||||
>
|
||||
<button
|
||||
aria-controls="tabPane-PROFILE"
|
||||
aria-selected={true}
|
||||
className="BaseButton-root Tab-button Tab-primary Tab-active coral coral-tabBar-tab coral-tabBar-myProfile"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
role="tab"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
My Profile
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<section
|
||||
aria-labelledby="tab-PROFILE"
|
||||
className="App-tabContent coral coral-myProfile"
|
||||
data-testid="current-tab-pane"
|
||||
id="tabPane-PROFILE"
|
||||
role="tabpanel"
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
|
||||
>
|
||||
Manage your account
|
||||
</h1>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root AccountSettingsContainer-root HorizontalGutter-full"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-spacing-5"
|
||||
data-testid="profile-changeUsername"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-double"
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignBaseline"
|
||||
>
|
||||
<div>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark coral coral-myUsername"
|
||||
>
|
||||
Username
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
>
|
||||
Passivo
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-myUsername-editButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root coral coral-myEmail HorizontalGutter-spacing-5"
|
||||
data-testid="profile-changeEmail"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignCenter"
|
||||
>
|
||||
<div>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark"
|
||||
>
|
||||
Email
|
||||
</h1>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex"
|
||||
>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
/>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextSecondary coral coral-myEmail-unverified"
|
||||
>
|
||||
(Unverified)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-myEmail-editButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-root CallOut-colorRegular coral coral-verifyEmail"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root coral coral-viewerBox"
|
||||
className="CallOut-inner"
|
||||
>
|
||||
<div
|
||||
className="Flex-flex Flex-halfItemGutter Flex-wrap gutter"
|
||||
className="Box-root Flex-root Flex-flex Flex-itemGutter gutter"
|
||||
>
|
||||
<div
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary UserBoxAuthenticated-userBoxText"
|
||||
>
|
||||
Signed in as
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary"
|
||||
<div>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-lg"
|
||||
>
|
||||
Passivo
|
||||
</span>
|
||||
.
|
||||
email
|
||||
</i>
|
||||
</div>
|
||||
<div
|
||||
className="Box-root Flex-root Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary UserBoxAuthenticated-userBoxText Flex-flex"
|
||||
>
|
||||
<span>
|
||||
Not you?
|
||||
</span>
|
||||
<div>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary Typography-gutterBottom"
|
||||
>
|
||||
Verify your email address
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
>
|
||||
An email has been sent to {$email} to verify your account.
|
||||
You must verify your new email address before it can be used
|
||||
to sign in to your account or to receive notifications.
|
||||
</p>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantRegular UserBoxAuthenticated-userBoxButton coral coral-viewerBox-logoutButton"
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantRegular ChangeEmailContainer-resendButton coral coral-verifyEmail-resendButton"
|
||||
data-color="primary"
|
||||
data-variant="regular"
|
||||
onBlur={[Function]}
|
||||
@@ -108,371 +134,79 @@ exports[`renders the empty settings pane 1`] = `
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Sign Out
|
||||
Resend verification
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
className="TabBar-root TabBar-secondary coral coral-tabBarSecondary coral-tabBarMyProfile"
|
||||
role="tablist"
|
||||
>
|
||||
<li
|
||||
className="Tab-root"
|
||||
id="tab-MY_COMMENTS"
|
||||
role="presentation"
|
||||
>
|
||||
<button
|
||||
aria-controls="tabPane-MY_COMMENTS"
|
||||
aria-selected={false}
|
||||
className="BaseButton-root Tab-button Tab-secondary coral coral-tabBarSecondary-tab coral-tabBarMyProfile-myComments"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
role="tab"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
My comments
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
className="Tab-root"
|
||||
id="tab-NOTIFICATIONS"
|
||||
role="presentation"
|
||||
>
|
||||
<button
|
||||
aria-controls="tabPane-NOTIFICATIONS"
|
||||
aria-selected={false}
|
||||
className="BaseButton-root Tab-button Tab-secondary coral coral-tabBarSecondary-tab coral-tabBarMyProfile-notifications"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
role="tab"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Notifications
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
className="Tab-root"
|
||||
id="tab-ACCOUNT"
|
||||
role="presentation"
|
||||
>
|
||||
<button
|
||||
aria-controls="tabPane-ACCOUNT"
|
||||
aria-selected={true}
|
||||
className="BaseButton-root Tab-button Tab-secondary Tab-active coral coral-tabBarSecondary-tab coral-tabBarMyProfile-settings"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
role="tab"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Account
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<section
|
||||
aria-labelledby="tab-ACCOUNT"
|
||||
className="coral coral-account"
|
||||
id="tabPane-ACCOUNT"
|
||||
role="tabpanel"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-oneAndAHalf"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary"
|
||||
>
|
||||
Manage your account
|
||||
</h1>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root AccountSettingsContainer-root HorizontalGutter-full"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-spacing-5"
|
||||
data-testid="profile-changeUsername"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignBaseline"
|
||||
>
|
||||
<div>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark coral coral-myUsername"
|
||||
>
|
||||
Username
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
>
|
||||
Passivo
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-myUsername-editButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root coral coral-myEmail HorizontalGutter-spacing-5"
|
||||
data-testid="profile-changeEmail"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignCenter"
|
||||
>
|
||||
<div>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark"
|
||||
>
|
||||
Email
|
||||
</h1>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex"
|
||||
>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
/>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextSecondary coral coral-myEmail-unverified"
|
||||
>
|
||||
(Unverified)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-myEmail-editButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="CallOut-root CallOut-colorRegular coral coral-verifyEmail"
|
||||
>
|
||||
<div
|
||||
className="CallOut-inner"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-itemGutter gutter"
|
||||
>
|
||||
<div>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="Icon-root Icon-lg"
|
||||
>
|
||||
email
|
||||
</i>
|
||||
</div>
|
||||
<div>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary Typography-gutterBottom"
|
||||
>
|
||||
Verify your email address
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
|
||||
>
|
||||
An email has been sent to {$email} to verify your account.
|
||||
You must verify your new email address before it can be used
|
||||
to sign in to your account or to receive notifications.
|
||||
</p>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeRegular Button-colorPrimary Button-variantRegular ChangeEmailContainer-resendButton coral coral-verifyEmail-resendButton"
|
||||
data-color="primary"
|
||||
data-variant="regular"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Resend verification
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="coral coral-myPassword"
|
||||
data-testid="profile-account-changePassword"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignCenter"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark"
|
||||
>
|
||||
Password
|
||||
</h1>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-myPassword-editButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="coral coral-ignoredCommenters"
|
||||
data-testid="profile-account-ignoredCommenters"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignCenter"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark"
|
||||
>
|
||||
Ignored Commenters
|
||||
</h1>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-ignoredComments-manageButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Manage
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="DownloadCommentsContainer-root coral coral-downloadCommentHistory"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignFlexStart"
|
||||
>
|
||||
<div
|
||||
className="DownloadCommentsContainer-content"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark DownloadCommentsContainer-title"
|
||||
>
|
||||
Download my comment history
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary DownloadCommentsContainer-description"
|
||||
>
|
||||
You will receive an email with a link to download your comment history.
|
||||
You can make
|
||||
<strong>
|
||||
one download request every 14 days.
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-downloadCommentHistory-requestButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Request
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="DeleteAccountContainer-root coral coral-deleteMyAccount"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignFlexStart"
|
||||
data-testid="profile-account-deleteAccount"
|
||||
>
|
||||
<div
|
||||
className="DeleteAccountContainer-content"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark DeleteAccountContainer-title"
|
||||
>
|
||||
Delete My Account
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary DeleteAccountContainer-section"
|
||||
>
|
||||
Deleting your account will permanently erase your profile and remove
|
||||
all your comments from this site.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-deleteMyAccount-requestButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Request
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
<div
|
||||
className="coral coral-myPassword"
|
||||
data-testid="profile-account-changePassword"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignCenter"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark"
|
||||
>
|
||||
Password
|
||||
</h1>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-myPassword-editButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="DeleteAccountContainer-root coral coral-deleteMyAccount"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-justifySpaceBetween Flex-alignFlexStart"
|
||||
data-testid="profile-account-deleteAccount"
|
||||
>
|
||||
<div
|
||||
className="DeleteAccountContainer-content"
|
||||
>
|
||||
<h1
|
||||
className="Box-root Typography-root Typography-heading2 Typography-colorTextDark DeleteAccountContainer-title"
|
||||
>
|
||||
Delete My Account
|
||||
</h1>
|
||||
<p
|
||||
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary DeleteAccountContainer-section"
|
||||
>
|
||||
Deleting your account will permanently erase your profile and remove
|
||||
all your comments from this site.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="BaseButton-root Button-root Button-sizeSmall Button-colorPrimary Button-variantOutlineFilled coral coral-deleteMyAccount-requestButton"
|
||||
data-color="primary"
|
||||
data-variant="outlineFilled"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Request
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -7,12 +7,10 @@ import {
|
||||
createResolversStub,
|
||||
CreateTestRendererParams,
|
||||
waitForElement,
|
||||
waitUntilThrow,
|
||||
within,
|
||||
} from "coral-framework/testHelpers";
|
||||
|
||||
import {
|
||||
commenters,
|
||||
settings,
|
||||
settingsWithoutLocalAuth,
|
||||
stories,
|
||||
@@ -46,19 +44,9 @@ async function createTestRenderer(
|
||||
},
|
||||
});
|
||||
|
||||
const ignoredCommenters = await waitForElement(() =>
|
||||
within(testRenderer.root).queryByTestID("profile-account-ignoredCommenters")
|
||||
);
|
||||
|
||||
const changePassword = within(testRenderer.root).queryByTestID(
|
||||
"profile-account-changePassword"
|
||||
);
|
||||
|
||||
return {
|
||||
testRenderer,
|
||||
context,
|
||||
ignoredCommenters,
|
||||
changePassword,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -66,18 +54,29 @@ it("renders the empty settings pane", async () => {
|
||||
const {
|
||||
testRenderer: { root },
|
||||
} = await createTestRenderer();
|
||||
expect(within(root).toJSON()).toMatchSnapshot();
|
||||
expect(await within(root).axe()).toHaveNoViolations();
|
||||
const account = await waitForElement(() =>
|
||||
within(root).getByTestID("profile-manageAccount")
|
||||
);
|
||||
expect(within(account).toJSON()).toMatchSnapshot();
|
||||
expect(await within(account).axe()).toHaveNoViolations();
|
||||
});
|
||||
|
||||
it("doesn't show the change password pane when local auth is disabled", async () => {
|
||||
const { changePassword } = await createTestRenderer({
|
||||
const {
|
||||
testRenderer: { root },
|
||||
} = await createTestRenderer({
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Query: {
|
||||
settings: () => settingsWithoutLocalAuth,
|
||||
},
|
||||
}),
|
||||
});
|
||||
const account = await waitForElement(() =>
|
||||
within(root).getByTestID("profile-manageAccount")
|
||||
);
|
||||
const changePassword = within(account).queryByTestID(
|
||||
"profile-account-changePassword"
|
||||
);
|
||||
expect(changePassword).toBeNull();
|
||||
});
|
||||
|
||||
@@ -143,57 +142,3 @@ it("render password change form", async () => {
|
||||
|
||||
expect(updatePassword.calledOnce).toBeTruthy();
|
||||
});
|
||||
|
||||
it("render empty ignored users list", async () => {
|
||||
const { ignoredCommenters } = await createTestRenderer();
|
||||
const editButton = within(ignoredCommenters).getByText("Manage");
|
||||
act(() => {
|
||||
editButton.props.onClick();
|
||||
});
|
||||
await waitForElement(() =>
|
||||
within(ignoredCommenters).getByText(
|
||||
"You are not currently ignoring anyone",
|
||||
{
|
||||
exact: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("render ignored users list", async () => {
|
||||
const { ignoredCommenters } = await createTestRenderer({
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Query: {
|
||||
viewer: () =>
|
||||
pureMerge<typeof viewer>(viewer, {
|
||||
ignoredUsers: [commenters[0], commenters[1]],
|
||||
}),
|
||||
},
|
||||
Mutation: {
|
||||
removeUserIgnore: ({ variables }) => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: commenters[0].id,
|
||||
});
|
||||
return {};
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
const editButton = within(ignoredCommenters).getByText("Manage");
|
||||
act(() => {
|
||||
editButton.props.onClick();
|
||||
});
|
||||
within(ignoredCommenters).getByText(commenters[0].username!);
|
||||
within(ignoredCommenters).getByText(commenters[1].username!);
|
||||
|
||||
// Stop ignoring first users.
|
||||
within(ignoredCommenters)
|
||||
.getAllByText("Stop ignoring", { selector: "button" })[0]
|
||||
.props.onClick();
|
||||
|
||||
// First user should dissappear from list.
|
||||
await waitUntilThrow(() =>
|
||||
within(ignoredCommenters).getByText(commenters[0].username!)
|
||||
);
|
||||
within(ignoredCommenters).getByText(commenters[1].username!);
|
||||
});
|
||||
|
||||
+61
-2
@@ -7,10 +7,11 @@ import {
|
||||
createResolversStub,
|
||||
CreateTestRendererParams,
|
||||
waitForElement,
|
||||
waitUntilThrow,
|
||||
within,
|
||||
} from "coral-framework/testHelpers";
|
||||
|
||||
import { settings, stories, viewerPassive } from "../fixtures";
|
||||
import { commenters, settings, stories, viewerPassive } from "../fixtures";
|
||||
import create from "./create";
|
||||
|
||||
const story = stories[0];
|
||||
@@ -32,16 +33,20 @@ async function createTestRenderer(
|
||||
params.resolvers
|
||||
),
|
||||
initLocalState: (localRecord, source, environment) => {
|
||||
localRecord.setValue("NOTIFICATIONS", "profileTab");
|
||||
localRecord.setValue("PREFERENCES", "profileTab");
|
||||
if (params.initLocalState) {
|
||||
params.initLocalState(localRecord, source, environment);
|
||||
}
|
||||
},
|
||||
});
|
||||
const ignoredCommenters = await waitForElement(() =>
|
||||
within(testRenderer.root).queryByTestID("profile-account-ignoredCommenters")
|
||||
);
|
||||
|
||||
return {
|
||||
testRenderer,
|
||||
context,
|
||||
ignoredCommenters,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,3 +147,57 @@ it("render notifications form", async () => {
|
||||
// The save button should now be disabled.
|
||||
expect(save.props.disabled).toEqual(true);
|
||||
});
|
||||
|
||||
it("render empty ignored users list", async () => {
|
||||
const { ignoredCommenters } = await createTestRenderer();
|
||||
const editButton = within(ignoredCommenters).getByText("Manage");
|
||||
act(() => {
|
||||
editButton.props.onClick();
|
||||
});
|
||||
await waitForElement(() =>
|
||||
within(ignoredCommenters).getByText(
|
||||
"You are not currently ignoring anyone",
|
||||
{
|
||||
exact: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("render ignored users list", async () => {
|
||||
const { ignoredCommenters } = await createTestRenderer({
|
||||
resolvers: createResolversStub<GQLResolver>({
|
||||
Query: {
|
||||
viewer: () =>
|
||||
pureMerge<typeof viewer>(viewer, {
|
||||
ignoredUsers: [commenters[0], commenters[1]],
|
||||
}),
|
||||
},
|
||||
Mutation: {
|
||||
removeUserIgnore: ({ variables }) => {
|
||||
expectAndFail(variables).toMatchObject({
|
||||
userID: commenters[0].id,
|
||||
});
|
||||
return {};
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
const editButton = within(ignoredCommenters).getByText("Manage");
|
||||
act(() => {
|
||||
editButton.props.onClick();
|
||||
});
|
||||
within(ignoredCommenters).getByText(commenters[0].username!);
|
||||
within(ignoredCommenters).getByText(commenters[1].username!);
|
||||
|
||||
// Stop ignoring first users.
|
||||
within(ignoredCommenters)
|
||||
.getAllByText("Stop ignoring", { selector: "button" })[0]
|
||||
.props.onClick();
|
||||
|
||||
// First user should dissappear from list.
|
||||
await waitUntilThrow(() =>
|
||||
within(ignoredCommenters).getByText(commenters[0].username!)
|
||||
);
|
||||
within(ignoredCommenters).getByText(commenters[1].username!);
|
||||
});
|
||||
@@ -41,6 +41,7 @@ export interface SSOUserProfile {
|
||||
username: string;
|
||||
badges?: string[];
|
||||
role?: GQLUSER_ROLE;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface SSOToken {
|
||||
@@ -64,8 +65,9 @@ export const SSOUserProfileSchema = Joi.object()
|
||||
username: Joi.string().required(),
|
||||
badges: Joi.array().items(Joi.string()),
|
||||
role: Joi.string().only(Object.values(GQLUSER_ROLE)),
|
||||
url: Joi.string().uri(),
|
||||
})
|
||||
.optionalKeys(["badges", "role"]);
|
||||
.optionalKeys(["badges", "role", "url"]);
|
||||
|
||||
export const SSOTokenSchema = Joi.object()
|
||||
.keys({
|
||||
@@ -91,7 +93,7 @@ export async function findOrCreateSSOUser(
|
||||
const {
|
||||
jti,
|
||||
exp,
|
||||
user: { id, email, username, badges, role },
|
||||
user: { id, email, username, badges, role, url },
|
||||
iat,
|
||||
} = decodedToken;
|
||||
|
||||
@@ -109,6 +111,7 @@ export async function findOrCreateSSOUser(
|
||||
type: "sso",
|
||||
id,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
if (!integration.allowRegistration) {
|
||||
// Registration is disabled, so we can't create the user user here.
|
||||
@@ -131,6 +134,7 @@ export async function findOrCreateSSOUser(
|
||||
id,
|
||||
username,
|
||||
role: role || GQLUSER_ROLE.COMMENTER,
|
||||
ssoURL: url,
|
||||
badges,
|
||||
email,
|
||||
emailVerified: true,
|
||||
|
||||
@@ -2098,6 +2098,11 @@ type User {
|
||||
roles: [ADMIN, MODERATOR]
|
||||
permit: [SUSPENDED, BANNED, PENDING_DELETION]
|
||||
)
|
||||
|
||||
"""
|
||||
ssoURL is the url for managing sso account
|
||||
"""
|
||||
ssoURL: String
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@@ -30,11 +30,12 @@ export function getSSOProfile(user: Pick<User, "profiles">) {
|
||||
|
||||
export function needsSSOUpdate(
|
||||
token: SSOUserProfile,
|
||||
user: Pick<User, "email" | "username" | "badges" | "role">
|
||||
user: Pick<User, "email" | "username" | "badges" | "role" | "ssoURL">
|
||||
) {
|
||||
return (
|
||||
user.email !== token.email ||
|
||||
user.username !== token.username ||
|
||||
(user.ssoURL && user.ssoURL !== token.url) ||
|
||||
(token.role && user.role !== token.role) ||
|
||||
!isEqual(user.badges, token.badges)
|
||||
);
|
||||
|
||||
@@ -392,6 +392,11 @@ export interface User extends TenantResource {
|
||||
*/
|
||||
badges?: string[];
|
||||
|
||||
/**
|
||||
* ssoURL is the url where a user can manage their sso account
|
||||
*/
|
||||
ssoURL?: string;
|
||||
|
||||
/**
|
||||
* emailVerificationID is used to store state regarding the verification state
|
||||
* of an email address to prevent replay attacks.
|
||||
@@ -485,6 +490,7 @@ export interface FindOrCreateUserInput {
|
||||
avatar?: string;
|
||||
email?: string;
|
||||
badges?: string[];
|
||||
ssoURL?: string;
|
||||
emailVerified?: boolean;
|
||||
role: GQLUSER_ROLE;
|
||||
profile: Profile;
|
||||
|
||||
@@ -188,6 +188,7 @@ comments-featured-replies = Replies
|
||||
profile-myCommentsTab = My Comments
|
||||
profile-myCommentsTab-comments = My comments
|
||||
profile-accountTab = Account
|
||||
profile-preferencesTab = Preferences
|
||||
|
||||
accountSettings-manage-account = Manage your account
|
||||
|
||||
|
||||
Reference in New Issue
Block a user