Browse Source

Feat(Account Settings): Layout and Notifications Panel (#6756)

Chris Clark 7 years ago
parent
commit
d2ab3cde1c

+ 6 - 0
.storybook/config.js

@@ -1,8 +1,13 @@
+import React from 'react';
 import {configure, setAddon, addDecorator} from '@storybook/react';
+import {ThemeProvider} from 'emotion-theming';
 import infoAddon, {setDefaults} from '@storybook/addon-info';
 import {withKnobs} from '@storybook/addon-knobs';
+import theme from '../src/sentry/static/sentry/app/utils/theme';
 import './storybook.less';
 
+const withTheme = storyFn => <ThemeProvider theme={theme}>{storyFn()}</ThemeProvider>;
+
 setDefaults({
   inline: true,
   header: false,
@@ -10,6 +15,7 @@ setDefaults({
 });
 setAddon(infoAddon);
 
+addDecorator(withTheme);
 addDecorator(withKnobs);
 // Use webpack's require.context to load modules dynamically
 // From https://storybook.js.org/basics/writing-stories/

+ 13 - 2
.storybook/webpack.config.js

@@ -1,8 +1,17 @@
 const path = require('path');
 const webpack = require('webpack');
 
-const staticPath = path.resolve(__dirname, '..', 'src', 'sentry', 'static', 'sentry');
-const componentPath = path.resolve(staticPath, 'app', 'components');
+const staticPath = path.resolve(
+  __dirname,
+  '..',
+  'src',
+  'sentry',
+  'static',
+  'sentry',
+  'app'
+);
+const componentPath = path.resolve(staticPath, 'components');
+const newSettingsPath = path.resolve(staticPath, 'views', 'settings', 'components');
 
 const sentryConfig = require('../webpack.config');
 const appConfig = sentryConfig[0];
@@ -63,6 +72,8 @@ module.exports = {
     extensions: appConfig.resolve.extensions,
     alias: Object.assign({}, appConfig.resolve.alias, {
       'sentry-ui': componentPath,
+      'settings-ui': newSettingsPath,
+      'application-root': staticPath,
     }),
   },
 };

+ 35 - 12
docs-ui/components/form.stories.js

@@ -4,11 +4,18 @@ import {storiesOf} from '@storybook/react';
 import {withInfo} from '@storybook/addon-info';
 import {action} from '@storybook/addon-actions';
 
-import {Form, TextField, PasswordField, BooleanField} from 'sentry-ui/forms';
+import {
+  Form as LegacyForm,
+  TextField,
+  PasswordField,
+  BooleanField,
+} from 'sentry-ui/forms';
+import RadioField from 'settings-ui/forms/radioField';
+import Form from 'settings-ui/forms/form';
 
 class UndoButton extends React.Component {
   static contextTypes = {
-    form: PropTypes.object
+    form: PropTypes.object,
   };
 
   handleClick = e => {
@@ -17,23 +24,27 @@ class UndoButton extends React.Component {
   };
 
   render() {
-    return <button type="button" onClick={this.handleClick}> Undo</button>;
+    return (
+      <button type="button" onClick={this.handleClick}>
+        Undo
+      </button>
+    );
   }
 }
 
 // eslint-disable-next-line
 storiesOf('Forms/Form', module)
-  .add('empty', withInfo('Empty form')(() => <Form onSubmit={action('submit')} />))
+  .add('empty', withInfo('Empty form')(() => <LegacyForm onSubmit={action('submit')} />))
   .add(
     'with Cancel',
     withInfo('Adds a "Cancel" button when `onCancel` is defined')(() => (
-      <Form onCancel={action('cancel')} onSubmit={action('submit')} />
+      <LegacyForm onCancel={action('cancel')} onSubmit={action('submit')} />
     ))
   )
   .add(
     'save on blur and undo',
     withInfo('Saves on blur and has undo')(() => (
-      <Form saveOnBlur allowUndo>
+      <LegacyForm saveOnBlur allowUndo>
         <TextField
           name="name"
           label="Team Name"
@@ -42,7 +53,7 @@ storiesOf('Forms/Form', module)
         />
         <TextField name="slug" label="Short name" placeholder="e.g. api-team" />
         <UndoButton />
-      </Form>
+      </LegacyForm>
     ))
   );
 
@@ -50,7 +61,7 @@ storiesOf('Forms/Fields', module)
   .add(
     'TextField',
     withInfo('Simple text input')(() => (
-      <Form saveOnBlur allowUndo>
+      <LegacyForm saveOnBlur allowUndo>
         <TextField
           name="name"
           label="Team Name"
@@ -58,22 +69,34 @@ storiesOf('Forms/Fields', module)
           required
         />
         <TextField name="slug" label="Short name" placeholder="e.g. api-team" />
-      </Form>
+      </LegacyForm>
     ))
   )
   .add(
     'PasswordField',
     withInfo('Password input')(() => (
-      <Form>
+      <LegacyForm>
         <PasswordField hasSavedValue name="password" label="password" />
-      </Form>
+      </LegacyForm>
     ))
   )
   .add(
     'BooleanField',
     withInfo('Boolean field (i.e. checkbox)')(() => (
-      <Form>
+      <LegacyForm>
         <BooleanField name="field" />
+      </LegacyForm>
+    ))
+  )
+  .add(
+    'RadioField',
+    withInfo('Radio field')(() => (
+      <Form>
+        <RadioField
+          name="radio"
+          label="Radio Field"
+          choices={[[0, 'Choice One'], [1, 'Choice Two'], [2, 'Choice Three']]}
+        />
       </Form>
     ))
   );

+ 71 - 0
src/sentry/static/sentry/app/data/forms/accountNotificationSettings.jsx

@@ -0,0 +1,71 @@
+const forms = [
+  {
+    title: 'Alerts',
+    fields: [
+      {
+        name: 'projectAlerts',
+        type: 'boolean',
+        label: 'Send Me Project Alerts',
+        help: 'Alerts are defined in [Project] » Project Settings » Alerts » Rules.',
+      },
+    ],
+  },
+
+  {
+    title: 'Workflow Notifications',
+    fields: [
+      {
+        name: 'workflowNotifications',
+        type: 'radio',
+        label: 'Send Me Workflow Notifications',
+        choices: [[0, 'Always'], [1, 'Only On Issues I Subscribe To'], [2, 'Never']],
+        help: 'E.g. changes in issue assignment, resolution status, and comments.',
+      },
+    ],
+  },
+
+  {
+    title: 'Weekly Reports',
+    fields: [
+      {
+        name: 'weeklyReports',
+        type: 'boolean',
+        label: 'Send Me Weekly Reports',
+        help: "Reports contain a summary of what's happened within your organization.",
+      },
+    ],
+  },
+
+  {
+    title: 'Deploy Notifications',
+    fields: [
+      {
+        name: 'deployNotifications',
+        type: 'radio',
+        label: 'Send Me Deploy Notifications',
+        choices: [[0, 'Always'], [1, 'Only On Deploys With My Commits'], [2, 'Never']],
+        help: 'Deploy emails include release, environment and commit overviews.',
+      },
+    ],
+  },
+
+  {
+    title: 'My Activity',
+    fields: [
+      {
+        name: 'personalActivity',
+        type: 'boolean',
+        label: 'Notify Me About My Own Activity',
+        help: 'Enable this to recieve notifications about your own actions on Sentry.',
+      },
+      {
+        name: 'claimUnassignedIssues',
+        type: 'boolean',
+        label: "Claim Unassigned Issues I've Resolved",
+        help: "You'll recieve notifications about any changes that happen afterwords.",
+      },
+    ],
+  },
+];
+
+export default forms;

+ 23 - 0
src/sentry/static/sentry/app/routes.jsx

@@ -3,6 +3,9 @@ import React from 'react';
 
 import AccountAuthorizations from './views/accountAuthorizations';
 import AccountLayout from './views/accountLayout';
+import AccountSettingsLayout from './views/settings/account/accountSettingsLayout';
+import AccountNotifications from './views/settings/account/accountNotifications';
+import AccountEmails from './views/settings/account/accountEmails';
 import AdminBuffer from './views/adminBuffer';
 import AdminLayout from './views/adminLayout';
 import AdminOrganizations from './views/adminOrganizations';
@@ -226,6 +229,23 @@ const orgSettingsRoutes = [
   />,
 ];
 
+const accountSettingsRoutes = [
+  <IndexRedirect key="account-settings-index" to="notifications/" />,
+
+  <Route
+    key="notifications/"
+    path="notifications/"
+    name="Notifications"
+    component={errorHandler(AccountNotifications)}
+  />,
+  <Route
+    key="emails/"
+    path="emails/"
+    name="Emails"
+    component={errorHandler(AccountEmails)}
+  />,
+];
+
 const projectSettingsRoutes = [
   <IndexRedirect key="projects-index" to="settings/" />,
   <Route
@@ -368,6 +388,9 @@ function routes() {
         component={errorHandler(SettingsWrapper)}
       >
         <IndexRoute component={errorHandler(SettingsIndex)} />
+        <Route path="account/" name="Account" component={AccountSettingsLayout}>
+          {accountSettingsRoutes}
+        </Route>
         <Route path="organization/">
           <IndexRoute component={errorHandler(OrganizationPicker)} />
 

+ 13 - 0
src/sentry/static/sentry/app/views/settings/account/accountEmails.jsx

@@ -0,0 +1,13 @@
+import React from 'react';
+
+import SettingsPageHeader from '../components/settingsPageHeader';
+
+const AccountEmails = () => {
+  return (
+    <div>
+      <SettingsPageHeader label="Emails" />
+    </div>
+  );
+};
+
+export default AccountEmails;

+ 21 - 0
src/sentry/static/sentry/app/views/settings/account/accountNotifications.jsx

@@ -0,0 +1,21 @@
+import React from 'react';
+
+import SettingsPageHeader from '../components/settingsPageHeader';
+import accountNotificationFields from '../../../data/forms/accountNotificationSettings';
+import Form from '../components/forms/form';
+import JsonForm from '../components/forms/jsonForm';
+
+const AccountNotifications = React.createClass({
+  render() {
+    return (
+      <div>
+        <SettingsPageHeader label="Notifications" />
+        <Form>
+          <JsonForm location={this.props.location} forms={accountNotificationFields} />
+        </Form>
+      </div>
+    );
+  },
+});
+
+export default AccountNotifications;

+ 19 - 0
src/sentry/static/sentry/app/views/settings/account/accountSettingsLayout.jsx

@@ -0,0 +1,19 @@
+import React from 'react';
+
+import SettingsLayout from '../settingsLayout';
+import AccountSettingsNavigation from './accountSettingsNavigation';
+
+class AccountSettingsLayout extends React.Component {
+  render() {
+    return (
+      <SettingsLayout
+        {...this.props}
+        renderNavigation={() => <AccountSettingsNavigation {...this.props} />}
+      >
+        {this.props.children}
+      </SettingsLayout>
+    );
+  }
+}
+
+export default AccountSettingsLayout;

+ 10 - 0
src/sentry/static/sentry/app/views/settings/account/accountSettingsNavigation.jsx

@@ -0,0 +1,10 @@
+import React from 'react';
+
+import SettingsNavigation from '../components/settingsNavigation';
+import navigationConfiguration from './navigationConfiguration';
+
+const AccountSettingsNavigation = () => {
+  return <SettingsNavigation navigationObjects={navigationConfiguration} />;
+};
+
+export default AccountSettingsNavigation;

+ 21 - 0
src/sentry/static/sentry/app/views/settings/account/navigationConfiguration.jsx

@@ -0,0 +1,21 @@
+import {t} from '../../../locale';
+
+const pathPrefix = '/settings/account';
+
+const accountNavigation = [
+  {
+    name: t('Account'),
+    items: [
+      {
+        path: `${pathPrefix}/notifications/`,
+        title: t('Notifications'),
+      },
+      {
+        path: `${pathPrefix}/emails/`,
+        title: t('Emails'),
+      },
+    ],
+  },
+];
+
+export default accountNavigation;

Some files were not shown because too many files changed in this diff