Repository: mdn/kuma

Issue:
T - Acct Settings: user details component form
# Acceptance criteria

- [ ] user form with name/static email/username/lang (slide 2)

# Important Note:
- The design comps are now out of date. See Acct Settings Gdoc/comments in this issue:

# Q:
- [ ] check language dropdown size


Discussion:
Moving the conversation here out of the Gdoc:

@schalkneethling mentioned moving email completely out of this form now since we have a specific component for managing this. Is that ok @atopal?
No, I think the email part should stay in the account settings, email in the side bar should be for managing receiving MDN emails, and should probably called "emails" instead. I might be wrong, but I think it would be confusing otherwise, since there is already a "subscription" menu item.
status: not started
Here is the latest design:

![Screenshot 2020-06-03 at 10 30 08](https://user-images.githubusercontent.com/10350960/83614346-5c89f580-a585-11ea-84b0-be5d14864f25.png)


Resolving patch:
diff --git a/kuma/accountsettings/views.py b/kuma/accountsettings/views.py
--- a/kuma/accountsettings/views.py
+++ b/kuma/accountsettings/views.py
@@ -1,5 +1,11 @@
+from django.conf import settings
 from django.shortcuts import render
 
 
 def index(request):
-    return render(request, "accountsettings/index.html")
+    # settings.SORTED_LANGUAGES is used by the select drop down
+    # on the account settings landing page
+    sorted_languages = dict(settings.SORTED_LANGUAGES)
+    return render(
+        request, "accountsettings/index.html", {"sorted_languages": sorted_languages}
+    )
diff --git a/kuma/settings/common.py b/kuma/settings/common.py
--- a/kuma/settings/common.py
+++ b/kuma/settings/common.py
@@ -674,6 +674,12 @@ def pipeline_one_scss(slug, **kwargs):
         ),
         "output_filename": "build/styles/accountsettings-titlebar.css",
     },
+    "accountsettings-user-details": {
+        "source_filenames": (
+            "styles/minimalist/components/accountsettings/user-details.scss",
+        ),
+        "output_filename": "build/styles/accountsettings-user-details.css",
+    },
     "mdn": {
         "source_filenames": ("styles/main.scss",),
         "output_filename": "build/styles/mdn.css",


Test patch:
diff --git a/kuma/javascript/src/accountsettings/components/language-select.test.jsx b/kuma/javascript/src/accountsettings/components/language-select.test.jsx
new file mode 100644
--- /dev/null
+++ b/kuma/javascript/src/accountsettings/components/language-select.test.jsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+
+import LanguageSelect from './language-select.jsx';
+
+const label = 'Testing';
+const name = 'test-name';
+
+const getProps = (label, name, defaultValue = 'en-US') => {
+    return {
+        defaultValue,
+        label,
+        name,
+        sortedLanguages: {
+            'en-US': 'English (US)',
+            'en-GB': 'English (UK)',
+            fr: 'French',
+            de: 'German',
+        },
+    };
+};
+
+describe('LanguageSelector', () => {
+    it('returns langauge select with appropriate label element', () => {
+        render(<LanguageSelect {...getProps(label, name)} />);
+
+        const fieldLabel = screen.getByText(label);
+
+        expect(fieldLabel).toHaveTextContent(label);
+        expect(fieldLabel).toHaveAttribute(
+            'for',
+            expect.stringContaining(name)
+        );
+    });
+
+    it('returns langauge select with appropriate select element', () => {
+        render(<LanguageSelect {...getProps(label, name)} />);
+
+        /*
+         * Being able to get the select element via the label text also
+         * confirms that our label is correctly associated with the
+         * select element.
+         */
+        const langSelect = screen.getByLabelText(label);
+
+        expect(langSelect).toHaveAttribute('id', expect.stringContaining(name));
+        expect(langSelect).toHaveAttribute(
+            'name',
+            expect.stringContaining(name)
+        );
+        expect(langSelect).toHaveValue('en-US');
+    });
+
+    it('returns langauge select with appropriate selected value', () => {
+        render(<LanguageSelect {...getProps(label, name, 'fr')} />);
+
+        const langSelect = screen.getByLabelText(label);
+
+        expect(langSelect).toHaveValue('fr');
+    });
+});
diff --git a/kuma/javascript/src/accountsettings/components/titlebar.test.jsx b/kuma/javascript/src/accountsettings/components/titlebar.test.jsx
--- a/kuma/javascript/src/accountsettings/components/titlebar.test.jsx
+++ b/kuma/javascript/src/accountsettings/components/titlebar.test.jsx
@@ -24,14 +24,9 @@ const setup = (mockData = {}) => {
 
 describe('Account Settings Titlebar', () => {
     it('renders the account settings landing page titlebar', () => {
-        const mockData = {
-            username: 'dino',
-        };
-        const { queryByText } = setup(mockData);
+        const { queryByText } = setup();
 
         expect(queryByText('Faulty Towers', { exact: false })).toBeNull();
-
         expect(queryByText(pageTitle, { exact: false })).toBeTruthy();
-        expect(queryByText(mockData.username, { exact: false })).toBeTruthy();
     });
 });
diff --git a/kuma/javascript/src/accountsettings/components/user-details.test.jsx b/kuma/javascript/src/accountsettings/components/user-details.test.jsx
new file mode 100644
--- /dev/null
+++ b/kuma/javascript/src/accountsettings/components/user-details.test.jsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+
+import UserDetails from './user-details.jsx';
+import UserProvider from '../../user-provider.jsx';
+
+const getProps = (mockData = {}) => {
+    const mockUserData = {
+        ...UserProvider.defaultUserData,
+        ...mockData,
+    };
+
+    return {
+        locale: 'en-US',
+        userData: mockUserData,
+        sortedLanguages: {
+            'en-US': 'English (US)',
+            'en-GB': 'English (UK)',
+            fr: 'French',
+            de: 'German',
+        },
+    };
+};
+
+describe('UserDetails', () => {
+    it('renders user details form', () => {
+        const username = 'testuser';
+        render(<UserDetails {...getProps({ username })} />);
+
+        const form = screen.getByRole('form');
+        const newsletterSubscribe = screen.getByRole('checkbox');
+
+        expect(form).toHaveFormValues({
+            'user-username': username,
+            'user-fullname': '',
+            'user-locale': 'en-US',
+        });
+        expect(newsletterSubscribe).not.toBeChecked();
+    });
+});
diff --git a/kuma/javascript/src/accountsettings/pages/index.test.jsx b/kuma/javascript/src/accountsettings/pages/index.test.jsx
new file mode 100644
--- /dev/null
+++ b/kuma/javascript/src/accountsettings/pages/index.test.jsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+
+import AccountSettingsLandingPage from './index.jsx';
+import UserProvider from '../../user-provider.jsx';
+
+const getTestData = (mockData = {}) => {
+    const mockProps = {
+        /* eslint-disable camelcase */
+        data: {
+            email: 'test@mozilla.com',
+            next_subscriber_number: 99,
+            sortedLanguages: {
+                'en-US': 'English (US)',
+                'en-GB': 'English (UK)',
+                fr: 'French',
+                de: 'German',
+            },
+        },
+        /* eslint-enable camelcase */
+    };
+    const mockUserData = {
+        ...UserProvider.defaultUserData,
+        ...mockData,
+    };
+    return {
+        mockProps,
+        mockUserData,
+    };
+};
+
+describe('Account Settings Landing Page', () => {
+    it('renders the account settings form for authenticated users', () => {
+        const mockData = getTestData({
+            isAuthenticated: true,
+        });
+
+        render(
+            <UserProvider.context.Provider value={mockData.mockUserData}>
+                <AccountSettingsLandingPage {...mockData.mockProps} />
+            </UserProvider.context.Provider>
+        );
+
+        expect(screen.queryByTestId('user-details-form')).toBeTruthy();
+        expect(screen.queryByText('Sign in')).not.toBeInTheDocument();
+    });
+
+    it('show singin link for unauthticated users', () => {
+        const mockData = getTestData();
+
+        render(
+            <UserProvider.context.Provider value={mockData.mockUserData}>
+                <AccountSettingsLandingPage {...mockData.mockProps} />
+            </UserProvider.context.Provider>
+        );
+
+        expect(screen.getByText('Sign in')).not.toBeNull();
+        expect(screen.queryByTestId('user-details-form')).toBeNull();
+    });
+});