diff --git a/src/components/Dropdown/Dropdown.types.tsx b/src/components/Dropdown/Dropdown.types.tsx index 285bb40e..72b3ca50 100644 --- a/src/components/Dropdown/Dropdown.types.tsx +++ b/src/components/Dropdown/Dropdown.types.tsx @@ -12,8 +12,8 @@ export interface DropdownProperties extends StateManagerProps { isDisabled?: boolean; isMulti?: boolean; label?: string; - labelClearAll: string; - onSelect: (event: OnChangeValue) => void; + labelClearAll?: string; + onSelect?: (event: OnChangeValue) => void; options: SelectOption[]; pillAlign?: 'bottom' | 'hide' | 'top'; // Display pills below/above the select input or hide them showClearAllSelectedButton?: boolean; // Show/Hide our custom 'Clear All...' button diff --git a/src/components/Layout/LayoutMain.tsx b/src/components/Layout/LayoutMain.tsx index 9e181072..60533b83 100644 --- a/src/components/Layout/LayoutMain.tsx +++ b/src/components/Layout/LayoutMain.tsx @@ -14,7 +14,7 @@ export const LayoutMain = ({ layout = '2-1', bleedbar }: LayoutMainProperties): JSX.Element => { - const cnames = ['content', `content__${layout}`, classes]; + const cnames = ['content', 'content_main', `content__${layout}`, classes]; if (bleedbar) cnames.push('content__bleedbar'); return ( diff --git a/src/prototypes/UserProfile/@Notes.md b/src/prototypes/UserProfile/@Notes.md new file mode 100644 index 00000000..f922b624 --- /dev/null +++ b/src/prototypes/UserProfile/@Notes.md @@ -0,0 +1,12 @@ +# Updates to DSR +- [New] Form component +- [New] Form -> Fieldset + - I think this would be used to wrap the groups of input fields +- [New] HelperText variant: block + - `.a-label_helper__block` +- [Improvement] TextInput + - Support `label` property so that Input and Label are automatically linked + - Support `helperText` property so that it doesn't need to be created separately + ``` + + ``` \ No newline at end of file diff --git a/src/prototypes/UserProfile/CompleteUserProfile.tsx b/src/prototypes/UserProfile/CompleteUserProfile.tsx new file mode 100644 index 00000000..03e06a2d --- /dev/null +++ b/src/prototypes/UserProfile/CompleteUserProfile.tsx @@ -0,0 +1,163 @@ +import { + Button, + Checkbox, + FooterCfGov, + Heading, + Layout, + Link, + Dropdown as Multiselect, + PageHeader +} from '~/src/index'; +import { DSRForm as Form } from './DSRForm'; +import { DSRLabeledInput as TextInput } from './DSRLabeledInput'; +import { SBLInstitutionCheckboxLabel as InstitutionLabel } from './SBLInstitutionCheckboxLabel'; + +const TextIntroduction = ({ heading, lead, body }): JSX.Element => ( +
+

{heading}

+

{lead}

+

{body}

+
+); + +const Well = ({ children }): JSX.Element => ( +
{children}
+); + +const FormActions = (): JSX.Element => ( +
+
+); + +const SectionIntroduction = ({ heading, body }): JSX.Element => ( + <> + {heading} +

{body}

+ +); + +const HelperText = ({ children }) =>

{children}

; + +const FieldGroup = ({ children }) =>
{children}
; + +export const CompleteUserProfile = ({ state, setState, onChange }) => ( + <> + + + + +
{ + e.preventDefault(); + setState(old => ({ ...old, submitted: true })); + }} + > + + + + + Type your first name and last name in the fields below. Your + email address is automatically populated from{' '} + Login.gov. + + } + /> + + + + + + + + + + + + + + + The following financial institution matches your email domain. + Check the box if you are authorized to file for this + institution. + + + + } + /> + + + If the financial institution you are authorized to file for is + not included above or if you are authorized to file for + additional institutions, search by LEI and select your + institution. + + + + + + + + +
+
+ + + +); diff --git a/src/prototypes/UserProfile/DSRForm.tsx b/src/prototypes/UserProfile/DSRForm.tsx new file mode 100644 index 00000000..00887326 --- /dev/null +++ b/src/prototypes/UserProfile/DSRForm.tsx @@ -0,0 +1,24 @@ +/* DSR component that hasn't been created yet */ + +import classnames from 'classnames'; + +export interface FormProperties { + id: string; + className?: string; + onSubmit: React.FormEvent; + children: JSX.Element[]; +} + +export const DSRForm = ({ + className = '', + children, + ...others +}: FormProperties): JSX.Element => { + const cnames = ['o-form', className]; + + return ( +
+ {children} +
+ ); +}; diff --git a/src/prototypes/UserProfile/DSRLabeledInput.tsx b/src/prototypes/UserProfile/DSRLabeledInput.tsx new file mode 100644 index 00000000..60cca72c --- /dev/null +++ b/src/prototypes/UserProfile/DSRLabeledInput.tsx @@ -0,0 +1,24 @@ +/* Potential improvement to DSR component */ + +import { HelperText } from '~/src/components/HelperText/HelperText'; +import type { TextInputProperties } from '~/src/components/TextInput/TextInput'; +import { Label, TextInput } from '~/src/index'; + +interface LIProperties extends TextInputProperties { + label: JSX.Element | string; + id: string; + helperText?: string; +} + +export const DSRLabeledInput = ({ + label, + id, + helperText, + ...others +}: LIProperties): JSX.Element => ( +
+ + {helperText ? {helperText} : null} + +
+); diff --git a/src/prototypes/UserProfile/Page.tsx b/src/prototypes/UserProfile/Page.tsx new file mode 100644 index 00000000..bc76c8bb --- /dev/null +++ b/src/prototypes/UserProfile/Page.tsx @@ -0,0 +1,19 @@ +/* Storybook prototype component */ + +interface PageProperties { + children: JSX.Element; +} + +/** + * Displays CFGov header and footer around provided page content + */ +export const Page = ({ children }: PageProperties): JSX.Element => children; +// export const Page = ({ children }: PageProperties): JSX.Element => ( +// <> +// +// +// {children} +// +// +// +// ); diff --git a/src/prototypes/UserProfile/SBLInstitutionCheckboxLabel.tsx b/src/prototypes/UserProfile/SBLInstitutionCheckboxLabel.tsx new file mode 100644 index 00000000..e6d39a4e --- /dev/null +++ b/src/prototypes/UserProfile/SBLInstitutionCheckboxLabel.tsx @@ -0,0 +1,25 @@ +/* SBL Specific */ + +interface ICLProperties { + name: string; + lei: string; + taxid: string; + agency: string; +} + +export const SBLInstitutionCheckboxLabel = ({ + name, + lei, + taxid, + agency +}: ICLProperties): JSX.Element => ( +
+ {name} +
+ LEI: {lei} +
+ Tax ID: {taxid} +
+ Agency Code: {agency} +
+); diff --git a/src/prototypes/UserProfile/UserProfile.less b/src/prototypes/UserProfile/UserProfile.less new file mode 100644 index 00000000..f3ca3e11 --- /dev/null +++ b/src/prototypes/UserProfile/UserProfile.less @@ -0,0 +1,68 @@ +/** Complete Profile **/ +.fieldset { + padding: 30px 25px; + margin-bottom: 30px; + background-color: #f7f8f9; + border: 1px solid #a2a3a4; + + & > *:not(:last-child) { + margin-bottom: 30px; + } +} + +.fieldset + h3 { + margin-top: 45px; +} + +.fieldset + .form-actions { + margin-top: 30px; +} + +.form-actions > * { + margin-right: 10px; +} + +.w-medium { + max-width: 770px; + margin: 0 auto; +} + +.text-introduction { + margin-bottom: 45px; +} + +/** Profile overview **/ +#profile-status { + .field { + margin-bottom: 30px; + } + + .m-notification { + margin: 30px auto 45px; + } + + h3 { + margin-bottom: 30px; + } + + .details { + h4 { + margin-bottom: 8px; + } + .institution-list { + padding: 0; + + .institution { + display: block; + + svg { + color: #21aa40; + } + + & > *:not(:last-child) { + margin-right: 10px; + } + } + } + } +} \ No newline at end of file diff --git a/src/prototypes/UserProfile/UserProfile.stories.tsx b/src/prototypes/UserProfile/UserProfile.stories.tsx new file mode 100644 index 00000000..2a2d29f3 --- /dev/null +++ b/src/prototypes/UserProfile/UserProfile.stories.tsx @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { useCallback, useState } from 'react'; +import { CompleteUserProfile } from './CompleteUserProfile'; +import { Page } from './Page'; +import './UserProfile.less'; +import { UserProfileStatus } from './UserProfileStatus'; + +const meta: Meta = { + title: 'Prototypes/User profile', + component: Page +}; + +export default meta; + +type Story = StoryObj; + +export interface UserProfilePageState { + submitted: boolean; + institutionSelected: boolean; + firstName: string; + lastName: string; + email: string; +} + +/** + * Wrapper to provide state management to the page + */ +const StatefulContainer = (): JSX.Element => { + const [state, setState] = useState({ + submitted: false, + institutionSelected: false, + firstName: 'Test', + lastName: 'User', + email: 'test.user@fintech1.com' + }); + + const onChange = useCallback( + (event: React.ChangeEvent) => { + const value = + event.target.type === 'checkbox' + ? event.target.checked + : event.target.value; + setState(old => ({ ...old, [event.target.id]: value })); + }, + [setState] + ); + + if (state.submitted) + return ; + + return ; +}; + +export const UserProfile: Story = { + args: { + children: + } +}; diff --git a/src/prototypes/UserProfile/UserProfileStatus.tsx b/src/prototypes/UserProfile/UserProfileStatus.tsx new file mode 100644 index 00000000..fd80cfb6 --- /dev/null +++ b/src/prototypes/UserProfile/UserProfileStatus.tsx @@ -0,0 +1,87 @@ +import type { Dispatch, SetStateAction } from 'react'; +import { useEffect } from 'react'; +import { + Button, + FooterCfGov, + Icon, + Layout, + Link, + Notification, + PageHeader +} from '~/src/index'; +import type { UserProfilePageState } from './UserProfile.stories'; + +interface PageProperties { + state: UserProfilePageState; + setState: Dispatch>; +} + +export const UserProfileStatus = ({ + state, + setState +}: PageProperties): JSX.Element => { + useEffect(() => window.scrollTo(0, 0), []); + + const onButtonClick = (event: MouseEvent): void => { + event.preventDefault(); + setState(old => ({ ...old, submitted: false })); + }; + + return ( + <> + + + + +
+

User profile submission status

+ + You have successfully completed your user profile and are + authorized to proceed to the data filing platform. If you need + further assistance please{' '} + submit a technical question. + +
+

User profile details

+
+

First name

+

{state.firstName}

+
+
+

Last name

+

{state.lastName}

+
+
+

Email address

+

+ {state.email} +

+
+
+

Associated financial institutions

+
    +
  • + + Approved + Bank 1 | TESTS6AFX2TESTXJ89VJ +
  • +
+
+
+
+
+
+
+
+ + + + ); +};