Skip to content

Commit

Permalink
feat: add user account settings page (#2167)
Browse files Browse the repository at this point in the history
Co-authored-by: Florent <florent.lagrede@gmail.com>
Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>
  • Loading branch information
3 people authored Oct 16, 2023
1 parent cc03e5c commit f1a1d77
Show file tree
Hide file tree
Showing 27 changed files with 1,071 additions and 423 deletions.
7 changes: 0 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,6 @@
"@graphql-codegen/typescript-graphql-files-modules": "1.18.1",
"@graphql-codegen/typescript-operations": "^1.18.0",
"@graphql-codegen/typescript-react-apollo": "2.2.4",
"@storybook/addon-actions": "^7.3.2",
"@storybook/addon-essentials": "^7.3.2",
"@storybook/addon-interactions": "^7.3.2",
"@storybook/addon-links": "^7.3.2",
"@storybook/react": "^7.3.2",
"@storybook/react-vite": "^7.3.2",
"@types/css-mediaquery": "^0.1.1",
"@types/jest": "27.0.3",
"@types/node": "13.1.1",
Expand All @@ -66,7 +60,6 @@
"patch-package": "^8.0.0",
"prettier": "2.4.1",
"shx": "^0.3.2",
"storybook": "^7.3.2",
"typescript": "^5.2.2"
},
"resolutions": {
Expand Down
47 changes: 47 additions & 0 deletions tailwind.common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// This file defines the common tailwind configuration to be used as the basis
// of each project's tailwind.config.js file.
module.exports = {
theme: {
colors: {
// Make sure these guidelines are followed when adding new colors: https://tailwindcss.com/docs/customizing-colors#using-css-variables
// Color variables should be added to tailwind.css.
'primary-main': 'rgb(var(--primary-main) / <alpha-value>)',
brand: {
400: 'rgb(var(--brand-400) / <alpha-value>)',
300: 'rgb(var(--brand-300) / <alpha-value>)',
},
grey: {
0: 'rgb(var(--grey-0) / <alpha-value>)',
100: 'rgb(var(--grey-100) / <alpha-value>)',
200: 'rgb(var(--grey-200) / <alpha-value>)',
300: 'rgb(var(--grey-300) / <alpha-value>)',
400: 'rgb(var(--grey-400) / <alpha-value>)',
},
},
spacing: {
// This spacing scale is based on the actual pixel values converted to REM values.
// This diverges from tailwind's standard pattern of defining its own spacing numbers
// but should be easier for developers trying to match spacing to designs in Figma.
0: '0',
3: '0.1875rem', // 3px
5: '0.3125rem', // 5px
10: '0.625rem', // 10px
15: '0.9375rem', // 15px
20: '1.25rem', // 20px
25: '1.5625rem', // 25px
30: '1.875rem', // 30px
40: '2.5rem', // 40px
50: '3.125rem', // 50px
60: '3.75rem', // 60px
},
extend: {},
},
plugins: [],
corePlugins: {
// Preflight needs to be disabled to avoid problems with MUI component styles.
// This has the side effect that some tailwind styles don't work as expected
// by default. For instance, in order for borders to show up at all,
// border-solid is needed whereas it would normally be set by default.
preflight: false,
},
};
22 changes: 22 additions & 0 deletions tailwind.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* This file defines the common tailwind CSS file to be imported by each project. */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
:root {
/*
Make sure these guidelines are followed: https://tailwindcss.com/docs/customizing-colors#using-css-variables
when adding new variables. Spaces must be used.
Colors are based off of https://www.figma.com/file/Md1Cl2B9eJi4NMzQgpbdMF/Dark-Mode?type=design&node-id=112-9885&mode=design&t=YKxx8fYvslM2TeLp-0.
*/
--brand-400: 79 181 115;
--brand-300: 123 199 150;
--grey-0: 255 255 255;
--grey-100: 250 250 250;
--grey-200: 239 239 239;
--grey-300: 210 213 217;
--grey-400: 143 143 143;
}
}
2 changes: 2 additions & 0 deletions web-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,14 @@
"@types/jest": "^29.0.0",
"@types/mapbox__mapbox-sdk": "^0.11.1",
"@types/react-lazyload": "^3.1.0",
"clsx": "^2.0.0",
"csstype": "^3.1.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"jest": "^28.1.3",
"prettier": "2.4.1",
"storybook-addon-react-router-v6": "^0.1.10",
"tailwind-merge": "^1.14.0",
"ts-jest": "^28.0.8"
}
}
51 changes: 51 additions & 0 deletions web-components/src/components/buttons/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useState } from 'react';

import copyTextToClipboard from '../../utils/copy';
import Banner from '../banner';
import { CopyIcon } from '../icons/CopyIcon';
import InfoTooltip from '../tooltip/InfoTooltip';

/** The props for the CopyButton component */
export interface CopyButtonProps {
/** The content to be copied to the clipboard */
content: string;

/** The text to be displayed in the tooltip */
tooltipText: string;

/** The text to be displayed in the toast banner */
toastText: string;
}

/** CopyButton is a component for copying text to the clipboard. It
* displays a copy icon that, when clicked, copies the content to the
* clipboard and displays a toast banner with the toastText.
*/
export const CopyButton = ({
content,
tooltipText,
toastText,
}: CopyButtonProps) => {
const [copied, setCopied] = useState(false);
return (
<div>
<InfoTooltip title={tooltipText} arrow placement="top">
<CopyIcon
onClick={() => {
copyTextToClipboard(content).then(() => {
setCopied(true);
});
}}
className="cursor-pointer hover:stroke-grey-400"
/>
</InfoTooltip>
{copied && (
<Banner
text={toastText}
duration={1000}
onClose={() => setCopied(false)}
/>
)}
</div>
);
};
7 changes: 7 additions & 0 deletions web-components/src/components/buttons/button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Box } from '@mui/material';

import { Flex } from '../box';
import ContainedButton from './ContainedButton';
import { CopyButton } from './CopyButton';
import { EditButton } from './EditButton';
import { ExpandButton } from './ExpandButton';
import OutlinedButton from './OutlinedButton';
Expand Down Expand Up @@ -83,3 +84,9 @@ export const textButton = (): JSX.Element => (
);

export const editButton = () => <EditButton onClick={() => {}} />;

export const copyButton = {
render: () => (
<CopyButton content="foo" tooltipText="Copy it" toastText="Copied!" />
),
};
13 changes: 13 additions & 0 deletions web-components/src/components/icons/CopyIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

/** An SVG copy icon which takes React SVG props */
export const CopyIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg width="24" height="24" fill="none" {...props}>
<path
fill="#000"
fillRule="evenodd"
d="M19 8v9H9.25a.25.25 0 0 1-.25-.25V3h5v4.25c0 .414.336.75.75.75H19Zm-4-4.586L18.287 7H15V3.414ZM3.25 5H7V1.25A.25.25 0 0 1 7.25 1h8.14c.07 0 .137.03.184.081l5.36 5.847a.25.25 0 0 1 .066.17V18.75a.25.25 0 0 1-.25.25H17v3.75a.25.25 0 0 1-.25.25H3.25a.25.25 0 0 1-.25-.25V5.25A.25.25 0 0 1 3.25 5ZM7 16.75V7H5v14h10v-2H7.25a.25.25 0 0 1-.25-.25v-2Z"
clipRule="evenodd"
/>
</svg>
);
2 changes: 2 additions & 0 deletions web-components/src/components/icons/icons.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import EeurIcon from './coins/EeurIcon';
import EvmosIcon from './coins/EvmosIcon';
import GravUsdcIcon from './coins/GravUsdcIcon';
import UsdcIcon from './coins/UsdcIcon';
import { CopyIcon } from './CopyIcon';
import CountingIcon from './CountingIcon';
import { CreditBatchIcon } from './CreditBatchIcon';
import { CreditBatchLightIcon } from './CreditBatchLightIcon';
Expand Down Expand Up @@ -197,6 +198,7 @@ export const allIcons = (): JSX.Element => (
<LabeledIcon icon={<CheckIcon />} label="CheckIcon" />
<LabeledIcon icon={<CloseIcon />} label="CloseIcon" />
<LabeledIcon icon={<CoBenefitsIcon />} label="CoBenefitsIcon" />
<LabeledIcon icon={<CopyIcon />} label="CopyIcon" />
<LabeledIcon icon={<CountingIcon />} label="CountingIcon" />
<LabeledIcon
icon={<CreditBatchIcon sx={{ color: '#4FB573' }} />}
Expand Down
13 changes: 13 additions & 0 deletions web-components/src/utils/styles/cn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

/**
* Merges class names and applies Tailwind CSS styles.
* clsx(...inputs) allows to use of various syntaxes to apply style (object, array, condition...).
* twMerge(...) ensures that the classes applied at the end will override previous ones in case of conflict.
* @param inputs - The class names to merge.
* @returns The merged class names with Tailwind CSS styles applied.
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(...inputs));
}
3 changes: 3 additions & 0 deletions web-marketplace/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,20 @@
"@types/use-analytics": "^0.0.0",
"@vitejs/plugin-react": "^4.0.0",
"@walletconnect/types": "^1.8.0",
"autoprefixer": "^10.4.16",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6",
"enzyme-to-json": "^3.6.1",
"eslint": "^8.42.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"postcss": "^8.4.31",
"react-test-renderer": "^16.12.0",
"require-context.macro": "^1.2.2",
"rollup-plugin-visualizer": "^5.9.2",
"source-map-explorer": "^2.5.2",
"storybook-addon-react-router-v6": "^0.1.10",
"tailwindcss": "^3.3.3",
"vite": "^4.4.9",
"vite-plugin-checker": "^0.6.0",
"vite-plugin-svgr": "^3.2.0",
Expand Down
6 changes: 6 additions & 0 deletions web-marketplace/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';

import ContainedButton from 'web-components/lib/components/buttons/ContainedButton';
import { CopyButton } from 'web-components/lib/components/buttons/CopyButton';
import OutlinedButton from 'web-components/lib/components/buttons/OutlinedButton';
import { Body } from 'web-components/lib/components/typography';
import CloseIcon from "web-components/lib/components/icons/CloseIcon";

/** ConnectField is used internally by the UserAccountSettings component to
* display a single social or wallet connection.
*/
export const ConnectField = ({
providerName,
connect,
disconnect,
address,
}: {
providerName: string;
connect?: () => void;
disconnect?: () => void;
address?: string;
}) => {
return (
<div className="flex flex-row justify-between flex-wrap gap-y-10">
<div className="flex flex-col gap-5">
<Body size="md" color="info.dark-grey">
{providerName}
</Body>
{address ? (
<AddressWidget address={address} />
) : (
<Body size="sm">{connect ? 'Disconnected' : 'Connected'}</Body>
)}
</div>
<div>
{connect ? (
<ContainedButton onClick={connect}>CONNECT</ContainedButton>
) : (
<OutlinedButton onClick={disconnect}>
<div className="flex flex-row gap-10 items-center">
<CloseIcon className="stroke-brand-400 w-[26px] h-[26px]" />
<div>DISCONNECT</div>
</div>
</OutlinedButton>
)}
</div>
</div>
);
};

const AddressWidget = ({ address }: { address: string }) => (
<div className="flex flex-row gap-5">
<Body size="sm">{address}</Body>
<CopyButton
content={address}
tooltipText="Copy address"
toastText="Address copied!"
/>
</div>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { action } from '@storybook/addon-actions';
import type { Meta, StoryObj } from '@storybook/react';

import { Title } from 'web-components/lib/components/typography';

import { UserAccountSettings } from './UserAccountSettings';

const meta: Meta<typeof UserAccountSettings> = {
title: 'Registry/Organisms/UserAccountSettings',
component: UserAccountSettings,
args: {
email: 'joemcnab@gmail.com',
socialProviders: [
{
providerName: 'Google',
connect: action('connect google'),
},
{
providerName: 'LinkedIn',
disconnect: action('disconnect linkedin'),
},
],
walletProvider: {
address: 'regenfoobar3792723djghsdg',
disconnect: action('disconnect wallet'),
},
},
};

export default meta;
type Story = StoryObj<typeof UserAccountSettings>;

export const Default: Story = {};

export const Width375: Story = {
name: 'Wrapped in a 375px mobile container',
render: args => (
<div className="bg-grey-100 p-50">
<div className="flex flex-col gap-[24px] w-[375px]">
<Title variant="h5">Settings</Title>
<div className="border border-grey-200 border-solid px-10 py-40 bg-grey-0">
<UserAccountSettings {...args} />
</div>
</div>
</div>
),
};

export const Width560: Story = {
name: 'Wrapped in a 560px container',
render: args => (
<div className="bg-grey-100 p-50">
<div className="flex flex-col gap-[24px] w-[560px]">
<Title variant="h3">Settings</Title>
<div className="border border-grey-200 border-solid px-50 py-50 bg-grey-0">
<UserAccountSettings {...args} />
</div>
</div>
</div>
),
};
Loading

0 comments on commit f1a1d77

Please sign in to comment.