From 466e7534c532ae9c9aa58f5408e547c77b43cfe0 Mon Sep 17 00:00:00 2001 From: ryanml Date: Thu, 6 Oct 2022 07:43:52 -0700 Subject: [PATCH 1/5] Fixing Identicon import paths (#16101) --- .../app/modals/contract-details-modal/contract-details-modal.js | 2 +- .../network-account-balance-header.js | 2 +- ui/components/ui/contract-token-values/contract-token-values.js | 2 +- ui/components/ui/new-network-info/new-network-info.js | 2 +- ui/pages/token-details/token-details-page.test.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/components/app/modals/contract-details-modal/contract-details-modal.js b/ui/components/app/modals/contract-details-modal/contract-details-modal.js index 818920afe2f7..dd291303f88a 100644 --- a/ui/components/app/modals/contract-details-modal/contract-details-modal.js +++ b/ui/components/app/modals/contract-details-modal/contract-details-modal.js @@ -9,7 +9,7 @@ import IconBlockExplorer from '../../../ui/icon/icon-block-explorer'; import Button from '../../../ui/button/button.component'; import Tooltip from '../../../ui/tooltip/tooltip'; import { useI18nContext } from '../../../../hooks/useI18nContext'; -import Identicon from '../../../ui/identicon/identicon.component'; +import Identicon from '../../../ui/identicon'; import { ellipsify } from '../../../../pages/send/send.utils'; import Popover from '../../../ui/popover'; import Typography from '../../../ui/typography'; diff --git a/ui/components/app/network-account-balance-header/network-account-balance-header.js b/ui/components/app/network-account-balance-header/network-account-balance-header.js index a688c9d693cd..217111c16eac 100644 --- a/ui/components/app/network-account-balance-header/network-account-balance-header.js +++ b/ui/components/app/network-account-balance-header/network-account-balance-header.js @@ -1,6 +1,6 @@ import React, { useContext } from 'react'; import PropTypes from 'prop-types'; -import Identicon from '../../ui/identicon/identicon.component'; +import Identicon from '../../ui/identicon'; import { DISPLAY, FLEX_DIRECTION, diff --git a/ui/components/ui/contract-token-values/contract-token-values.js b/ui/components/ui/contract-token-values/contract-token-values.js index 1b2635ad6576..3288983e84f2 100644 --- a/ui/components/ui/contract-token-values/contract-token-values.js +++ b/ui/components/ui/contract-token-values/contract-token-values.js @@ -6,7 +6,7 @@ import IconBlockExplorer from '../icon/icon-block-explorer'; import Box from '../box/box'; import Tooltip from '../tooltip/tooltip'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import Identicon from '../identicon/identicon.component'; +import Identicon from '../identicon'; import Typography from '../typography/typography'; import { FONT_WEIGHT, diff --git a/ui/components/ui/new-network-info/new-network-info.js b/ui/components/ui/new-network-info/new-network-info.js index 9b9b7e674302..bedddb904da6 100644 --- a/ui/components/ui/new-network-info/new-network-info.js +++ b/ui/components/ui/new-network-info/new-network-info.js @@ -4,7 +4,7 @@ import { useHistory } from 'react-router-dom'; import { I18nContext } from '../../../contexts/i18n'; import Popover from '../popover'; import Button from '../button'; -import Identicon from '../identicon/identicon.component'; +import Identicon from '../identicon'; import Box from '../box'; import { ALIGN_ITEMS, diff --git a/ui/pages/token-details/token-details-page.test.js b/ui/pages/token-details/token-details-page.test.js index c4d1ca53c5ba..766a0607b6d6 100644 --- a/ui/pages/token-details/token-details-page.test.js +++ b/ui/pages/token-details/token-details-page.test.js @@ -2,7 +2,7 @@ import React from 'react'; import configureMockStore from 'redux-mock-store'; import { fireEvent } from '@testing-library/react'; import { renderWithProvider } from '../../../test/lib/render-helpers'; -import Identicon from '../../components/ui/identicon/identicon.component'; +import Identicon from '../../components/ui/identicon'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import TokenDetailsPage from './token-details-page'; From 6918bff291ead9b2ca2ff1ce9a8e82371d1cf333 Mon Sep 17 00:00:00 2001 From: Adnan Sahovic <63151811+adnansahovic@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:48:08 +0200 Subject: [PATCH 2/5] Created the NFT component for single NFT allowance (#15825) * Created the NFT component for single NFT allowance * modified NftInfo component * added assetName --- app/_locales/en/messages.json | 3 + ui/components/ui/nft-info/index.js | 1 + ui/components/ui/nft-info/index.scss | 9 +++ ui/components/ui/nft-info/nft-info.js | 64 +++++++++++++++++++ ui/components/ui/nft-info/nft-info.stories.js | 29 +++++++++ ui/components/ui/ui-components.scss | 1 + 6 files changed, 107 insertions(+) create mode 100644 ui/components/ui/nft-info/index.js create mode 100644 ui/components/ui/nft-info/index.scss create mode 100644 ui/components/ui/nft-info/nft-info.js create mode 100644 ui/components/ui/nft-info/nft-info.stories.js diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 04d76f58c37f..2ffc886b6da4 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -4251,6 +4251,9 @@ "message": "Verify this token on $1 and make sure this is the token you want to trade.", "description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\"" }, + "view": { + "message": "View" + }, "viewAccount": { "message": "View account" }, diff --git a/ui/components/ui/nft-info/index.js b/ui/components/ui/nft-info/index.js new file mode 100644 index 000000000000..f78c5a99f449 --- /dev/null +++ b/ui/components/ui/nft-info/index.js @@ -0,0 +1 @@ +export { default } from './nft-info'; diff --git a/ui/components/ui/nft-info/index.scss b/ui/components/ui/nft-info/index.scss new file mode 100644 index 000000000000..8e87ed280585 --- /dev/null +++ b/ui/components/ui/nft-info/index.scss @@ -0,0 +1,9 @@ +.nft-info { + &__content { + width: 100%; + } + + &__button { + padding: 0; + } +} diff --git a/ui/components/ui/nft-info/nft-info.js b/ui/components/ui/nft-info/nft-info.js new file mode 100644 index 000000000000..41c44947d0b8 --- /dev/null +++ b/ui/components/ui/nft-info/nft-info.js @@ -0,0 +1,64 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; +import { I18nContext } from '../../../contexts/i18n'; +import Box from '../box'; +import Typography from '../typography'; +import { + COLORS, + DISPLAY, + FONT_WEIGHT, + TYPOGRAPHY, +} from '../../../helpers/constants/design-system'; +import Identicon from '../identicon'; +import Button from '../button'; + +export default function NftInfo({ assetName, tokenAddress, tokenId }) { + const t = useContext(I18nContext); + + return ( + + + + + + + + {assetName} + + + {t('tokenId')} #{tokenId} + + + + + + + + ); +} + +NftInfo.propTypes = { + assetName: PropTypes.string, + tokenAddress: PropTypes.string, + tokenId: PropTypes.string, +}; diff --git a/ui/components/ui/nft-info/nft-info.stories.js b/ui/components/ui/nft-info/nft-info.stories.js new file mode 100644 index 000000000000..e13a7415ec9b --- /dev/null +++ b/ui/components/ui/nft-info/nft-info.stories.js @@ -0,0 +1,29 @@ +import React from 'react'; +import NftInfo from './nft-info'; + +export default { + title: 'Components/UI/NftInfo', + id: __filename, + argTypes: { + assetName: { + control: { type: 'text' }, + }, + tokenAddress: { + control: { type: 'text' }, + }, + tokenId: { + control: { type: 'text' }, + }, + }, + args: { + assetName: 'Catnip Spicewight', + tokenAddress: '0xa3aee8bce55beea1951ef834b99f3ac60d1abeeb', + tokenId: '112233', + }, +}; + +export const DefaultStory = (args) => { + return ; +}; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/ui/ui-components.scss b/ui/components/ui/ui-components.scss index 2f004c47e291..8cc0e8a0b5c0 100644 --- a/ui/components/ui/ui-components.scss +++ b/ui/components/ui/ui-components.scss @@ -62,3 +62,4 @@ @import 'disclosure/disclosure'; @import 'deprecated-test-networks/index.scss'; @import 'contract-token-values/index.scss'; +@import 'nft-info/index.scss'; From 055a7c52c00cb3cecda314d99d7dff39f8cb7072 Mon Sep 17 00:00:00 2001 From: George Marshall Date: Thu, 6 Oct 2022 12:41:22 -0700 Subject: [PATCH 3/5] Adding `TextFieldBase` component (#16043) * Adding TextInputBase component * Removing keyup and keydown props, tests and docs * removing showClear from stories * removing unneeded css * simplifying uncontrolled vs controlled to work * Fortifying maxLength test * Lint fix for test * Doc, style and prop updates * Updating constant names with 'base' * Adding a background color * Adding a background color to input --- .../component-library-components.scss | 1 + .../text-field-base/README.mdx | 298 +++++++++++++++ .../text-field-base/index.js | 5 + .../text-field-base.constants.js | 12 + .../text-field-base/text-field-base.js | 250 ++++++++++++ .../text-field-base/text-field-base.scss | 52 +++ .../text-field-base.stories.js | 361 ++++++++++++++++++ .../text-field-base/text-field-base.test.js | 213 +++++++++++ 8 files changed, 1192 insertions(+) create mode 100644 ui/components/component-library/text-field-base/README.mdx create mode 100644 ui/components/component-library/text-field-base/index.js create mode 100644 ui/components/component-library/text-field-base/text-field-base.constants.js create mode 100644 ui/components/component-library/text-field-base/text-field-base.js create mode 100644 ui/components/component-library/text-field-base/text-field-base.scss create mode 100644 ui/components/component-library/text-field-base/text-field-base.stories.js create mode 100644 ui/components/component-library/text-field-base/text-field-base.test.js diff --git a/ui/components/component-library/component-library-components.scss b/ui/components/component-library/component-library-components.scss index 97c7320a4cb8..85c2f84c6fc5 100644 --- a/ui/components/component-library/component-library-components.scss +++ b/ui/components/component-library/component-library-components.scss @@ -6,3 +6,4 @@ @import 'button-primary/button-primary'; @import 'icon/icon'; @import 'text/text'; +@import 'text-field-base/text-field-base'; diff --git a/ui/components/component-library/text-field-base/README.mdx b/ui/components/component-library/text-field-base/README.mdx new file mode 100644 index 000000000000..a19fdbab1d1e --- /dev/null +++ b/ui/components/component-library/text-field-base/README.mdx @@ -0,0 +1,298 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; + +import { TextFieldBase } from './text-field-base'; + +### This is a base component. It should not be used in your feature code directly but as a "base" for other UI components + +# TextFieldBase + +The `TextFieldBase` is the base component for all text fields. It should not be used directly. It functions as both a uncontrolled and controlled input. + + + + + +## Props + +The `TextFieldBase` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props) component props + + + +### Size + +Use the `size` prop to set the height of the `TextFieldBase`. + +Possible sizes include: + +- `sm` 32px +- `md` 40px +- `lg` 48px + +Defaults to `md` + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; +import { SIZES } from '../../../helpers/constants/design-system'; + + + + +``` + +### Type + +Use the `type` prop to change the type of input. + +Possible types include: + +- `text` +- `number` +- `password` + +Defaults to `text`. + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + + // (Default) + + +``` + +### Truncate + +Use the `truncate` prop to truncate the text of the the `TextFieldBase` + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +; +``` + +### Left Accessory Right Accessory + +Use the `leftAccessory` and `rightAccessory` props to add components such as icons or buttons to either side of the `TextFieldBase`. + + + + + +```jsx +import { COLORS, SIZES } from '../../../helpers/constants/design-system'; +import { Icon, ICON_NAMES } from '../../ui/component-library/icons'; + +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + + + } +/> + + + + + } +/> + +} + rightAccessory={ + // TODO: replace with ButtonIcon + + } +/> + + + } + rightAccessory={ + // TODO: replace with ButtonLink + + } +/> +``` + +### Input Ref + +Use the `inputRef` prop to access the ref of the `` html element of `TextFieldBase`. This is useful for focusing the input from a button or other component. + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +const inputRef = useRef(null); +const [value, setValue] = useState(''); +const handleOnClick = () => { + inputRef.current.focus(); +}; +const handleOnChange = (e) => { + setValue(e.target.value); +}; + + +// TODO: replace with Button component + + Edit + +``` + +### Auto Complete + +Use the `autoComplete` prop to set the autocomplete html attribute. It allows the browser to predict the value based on earlier typed values. + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +; +``` + +### Auto Focus + +Use the `autoFocus` prop to focus the `TextFieldBase` during the first mount + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +; +``` + +### Default Value + +Use the `defaultValue` prop to set the default value of the `TextFieldBase` + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +; +``` + +### Disabled + +Use the `disabled` prop to set the disabled state of the `TextFieldBase` + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +; +``` + +### Error + +Use the `error` prop to set the error state of the `TextFieldBase` + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +; +``` + +### Max Length + +Use the `maxLength` prop to set the maximum allowed input characters for the `TextFieldBase` + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +; +``` + +### Read Only + +Use the `readOnly` prop to set the `TextFieldBase` to read only + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +; +``` + +### Required + +Use the `required` prop to set the `TextFieldBase` to required. Currently there is no visual difference to the `TextFieldBase` when required. + + + + + +```jsx +import { TextFieldBase } from '../../ui/component-library/text-field-base'; + +// Currently no visual difference +; +``` diff --git a/ui/components/component-library/text-field-base/index.js b/ui/components/component-library/text-field-base/index.js new file mode 100644 index 000000000000..748a4de127e8 --- /dev/null +++ b/ui/components/component-library/text-field-base/index.js @@ -0,0 +1,5 @@ +export { TextFieldBase } from './text-field-base'; +export { + TEXT_FIELD_BASE_SIZES, + TEXT_FIELD_BASE_TYPES, +} from './text-field-base.constants'; diff --git a/ui/components/component-library/text-field-base/text-field-base.constants.js b/ui/components/component-library/text-field-base/text-field-base.constants.js new file mode 100644 index 000000000000..decdb364a11b --- /dev/null +++ b/ui/components/component-library/text-field-base/text-field-base.constants.js @@ -0,0 +1,12 @@ +import { SIZES } from '../../../helpers/constants/design-system'; + +export const TEXT_FIELD_BASE_SIZES = { + SM: SIZES.SM, + MD: SIZES.MD, + LG: SIZES.LG, +}; +export const TEXT_FIELD_BASE_TYPES = { + TEXT: 'text', + NUMBER: 'number', + PASSWORD: 'password', +}; diff --git a/ui/components/component-library/text-field-base/text-field-base.js b/ui/components/component-library/text-field-base/text-field-base.js new file mode 100644 index 000000000000..909dae6592c5 --- /dev/null +++ b/ui/components/component-library/text-field-base/text-field-base.js @@ -0,0 +1,250 @@ +import React, { useState, useRef, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { + DISPLAY, + SIZES, + ALIGN_ITEMS, + TEXT, + COLORS, +} from '../../../helpers/constants/design-system'; + +import Box from '../../ui/box'; + +import { Text } from '../text'; + +import { + TEXT_FIELD_BASE_SIZES, + TEXT_FIELD_BASE_TYPES, +} from './text-field-base.constants'; + +export const TextFieldBase = ({ + autoComplete, + autoFocus, + className, + defaultValue, + disabled, + error, + id, + inputProps, + inputRef, + leftAccessory, + rightAccessory, + maxLength, + name, + onBlur, + onChange, + onClick, + onFocus, + placeholder, + readOnly, + required, + size = SIZES.MD, + type = 'text', + truncate, + value, + ...props +}) => { + const internalInputRef = useRef(null); + const [focused, setFocused] = useState(false); + + useEffect(() => { + // The blur won't fire when the disabled state is set on a focused input. + // We need to set the focused state manually. + if (disabled) { + setFocused(false); + } + }, [disabled]); + + const handleClick = (event) => { + const { current } = internalInputRef; + + if (current) { + current.focus(); + setFocused(true); + } + + if (onClick) { + onClick(event); + } + }; + + const handleFocus = (event) => { + setFocused(true); + onFocus && onFocus(event); + }; + + const handleBlur = (event) => { + setFocused(false); + onBlur && onBlur(event); + }; + + const handleInputRef = (ref) => { + internalInputRef.current = ref; + if (inputRef && inputRef.current !== undefined) { + inputRef.current = ref; + } else if (typeof inputRef === 'function') { + inputRef(ref); + } + }; + + return ( + + {leftAccessory} + + {rightAccessory} + + ); +}; + +TextFieldBase.propTypes = { + /** + * Autocomplete allows the browser to predict the value based on earlier typed values + */ + autoComplete: PropTypes.string, + /** + * If `true`, the input will be focused during the first mount. + */ + autoFocus: PropTypes.bool, + /** + * An additional className to apply to the text-field-base + */ + className: PropTypes.string, + /** + * The default input value, useful when not controlling the component. + */ + defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + /** + * If `true`, the input will be disabled. + */ + disabled: PropTypes.bool, + /** + * If `true`, the input will indicate an error + */ + error: PropTypes.bool, + /** + * The id of the `input` element. + */ + id: PropTypes.string, + /** + * Attributes applied to the `input` element. + */ + inputProps: PropTypes.object, + /** + * Component to appear on the left side of the input + */ + leftAccessory: PropTypes.node, + /** + * Component to appear on the right side of the input + */ + rightAccessory: PropTypes.node, + /** + * Use inputRef to pass a ref to the html input element. + */ + inputRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + /** + * Max number of characters to allow + */ + maxLength: PropTypes.number, + /** + * Name attribute of the `input` element. + */ + name: PropTypes.string, + /** + * Callback fired on blur + */ + onBlur: PropTypes.func, + /** + * Callback fired when the value is changed. + */ + onChange: PropTypes.func, + /** + * Callback fired on focus + */ + onFocus: PropTypes.func, + /** + * The short hint displayed in the input before the user enters a value. + */ + placeholder: PropTypes.string, + /** + * It prevents the user from changing the value of the field (not from interacting with the field). + */ + readOnly: PropTypes.bool, + /** + * If `true`, the input will be required. Currently no visual difference is shown. + */ + required: PropTypes.bool, + /** + * The size of the text field. Changes the height of the component + * Accepts SM(32px), MD(40px), LG(48px) + */ + size: PropTypes.oneOf(Object.values(TEXT_FIELD_BASE_SIZES)), + /** + * Type of the input element. Can be TEXT_FIELD_BASE_TYPES.TEXT, TEXT_FIELD_BASE_TYPES.PASSWORD, TEXT_FIELD_BASE_TYPES.NUMBER + * Defaults to TEXT_FIELD_BASE_TYPES.TEXT ('text') + */ + type: PropTypes.oneOf(Object.values(TEXT_FIELD_BASE_TYPES)), + /** + * The input value, required for a controlled component. + */ + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + /** + * TextFieldBase accepts all the props from Box + */ + ...Box.propTypes, +}; + +TextFieldBase.displayName = 'TextFieldBase'; diff --git a/ui/components/component-library/text-field-base/text-field-base.scss b/ui/components/component-library/text-field-base/text-field-base.scss new file mode 100644 index 000000000000..6672506f5f6f --- /dev/null +++ b/ui/components/component-library/text-field-base/text-field-base.scss @@ -0,0 +1,52 @@ +.mm-text-field-base { + --text-field-base-height: var(--size, 40px); + + &--size-sm { + --size: 32px; + } + + &--size-md { + --size: 40px; + } + + &--size-lg { + --size: 48px; + } + + height: var(--text-field-base-height); + border-color: var(--color-border-default); + + &--focused { + border-color: var(--color-primary-default); + } + + &--error { + border-color: var(--color-error-default); + } + + &--disabled { + opacity: 0.5; + border-color: var(--color-border-default); + } + + // truncates text with ellipsis + &--truncate .mm-text-field-base__input { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &__input { + border: none; + height: 100%; + flex-grow: 1; + box-sizing: content-box; + margin: 0; + padding: 0; + + &:focus, + &:focus-visible { + outline: none; + } + } +} diff --git a/ui/components/component-library/text-field-base/text-field-base.stories.js b/ui/components/component-library/text-field-base/text-field-base.stories.js new file mode 100644 index 000000000000..f30e5cef5416 --- /dev/null +++ b/ui/components/component-library/text-field-base/text-field-base.stories.js @@ -0,0 +1,361 @@ +import React, { useState, useRef } from 'react'; + +import { + SIZES, + DISPLAY, + COLORS, + FLEX_DIRECTION, +} from '../../../helpers/constants/design-system'; +import Box from '../../ui/box/box'; + +import { Icon, ICON_NAMES } from '../icon'; +import { AvatarToken } from '../avatar-token'; + +import { + TEXT_FIELD_BASE_SIZES, + TEXT_FIELD_BASE_TYPES, +} from './text-field-base.constants'; +import { TextFieldBase } from './text-field-base'; +import README from './README.mdx'; + +const marginSizeControlOptions = [ + undefined, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 'auto', +]; + +export default { + title: 'Components/ComponentLibrary/TextFieldBase', + id: __filename, + component: TextFieldBase, + parameters: { + docs: { + page: README, + }, + }, + argTypes: { + autoComplete: { + control: 'boolean', + }, + autoFocus: { + control: 'boolean', + }, + className: { + control: 'text', + }, + defaultValue: { + control: 'text', + }, + disabled: { + control: 'boolean', + }, + error: { + control: 'boolean', + }, + id: { + control: 'text', + }, + inputProps: { + control: 'object', + }, + leftAccessory: { + control: 'text', + }, + maxLength: { + control: 'number', + }, + name: { + control: 'text', + }, + onBlur: { + action: 'onBlur', + }, + onChange: { + action: 'onChange', + }, + onClick: { + action: 'onClick', + }, + onFocus: { + action: 'onFocus', + }, + placeholder: { + control: 'text', + }, + readOnly: { + control: 'boolean', + }, + required: { + control: 'boolean', + }, + rightAccessory: { + control: 'text', + }, + size: { + control: 'select', + options: Object.values(TEXT_FIELD_BASE_SIZES), + }, + type: { + control: 'select', + options: Object.values(TEXT_FIELD_BASE_TYPES), + }, + value: { + control: 'text', + }, + marginTop: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginRight: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginBottom: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + marginLeft: { + options: marginSizeControlOptions, + control: 'select', + table: { category: 'box props' }, + }, + }, + args: { + placeholder: 'Placeholder...', + autoFocus: false, + defaultValue: '', + disabled: false, + error: false, + id: '', + readOnly: false, + required: false, + size: SIZES.MD, + type: 'text', + truncate: false, + }, +}; + +const Template = (args) => ; + +export const DefaultStory = Template.bind({}); +DefaultStory.storyName = 'Default'; + +export const Size = (args) => { + return ( + + + + + + ); +}; + +export const Type = (args) => ( + + + + + +); + +export const Truncate = Template.bind({}); +Truncate.args = { + placeholder: 'Truncate', + value: 'Truncated text when truncate and width is set', + truncate: true, + style: { width: 240 }, +}; + +export const LeftAccessoryRightAccessory = (args) => { + const [value, setValue] = useState({ + search: '', + metaMask: '', + address: '0x514910771af9ca656af840dff83e8264ecf986ca', + amount: 1, + }); + return ( + + setValue({ ...value, search: e.target.value })} + leftAccessory={ + + } + /> + setValue({ ...value, metaMask: e.target.value })} + placeholder="MetaMask" + rightAccessory={ + + } + /> + setValue({ ...value, address: e.target.value })} + truncate + leftAccessory={} + rightAccessory={ + + } + /> + setValue({ ...value, amount: e.target.value })} + type="number" + leftAccessory={ + + } + rightAccessory={ + + } + /> + + ); +}; + +export const InputRef = (args) => { + const inputRef = useRef(null); + const [value, setValue] = useState(''); + const handleOnClick = () => { + inputRef.current.focus(); + }; + const handleOnChange = (e) => { + setValue(e.target.value); + }; + return ( + <> + + + Edit + + + ); +}; + +export const AutoComplete = Template.bind({}); +AutoComplete.args = { + autoComplete: true, + type: 'password', + placeholder: 'Enter password', +}; + +export const AutoFocus = Template.bind({}); +AutoFocus.args = { autoFocus: true }; + +export const DefaultValue = Template.bind({}); +DefaultValue.args = { defaultValue: 'Default value' }; + +export const Disabled = Template.bind({}); +Disabled.args = { disabled: true }; + +export const ErrorStory = Template.bind({}); +ErrorStory.args = { error: true }; +ErrorStory.storyName = 'Error'; + +export const MaxLength = Template.bind({}); +MaxLength.args = { maxLength: 10, placeholder: 'Max length 10' }; + +export const ReadOnly = Template.bind({}); +ReadOnly.args = { readOnly: true, value: 'Read only' }; + +export const Required = Template.bind({}); +Required.args = { required: true, placeholder: 'Required' }; diff --git a/ui/components/component-library/text-field-base/text-field-base.test.js b/ui/components/component-library/text-field-base/text-field-base.test.js new file mode 100644 index 000000000000..37f190cfdc19 --- /dev/null +++ b/ui/components/component-library/text-field-base/text-field-base.test.js @@ -0,0 +1,213 @@ +/* eslint-disable jest/require-top-level-describe */ +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { SIZES } from '../../../helpers/constants/design-system'; + +import { TextFieldBase } from './text-field-base'; + +describe('TextFieldBase', () => { + it('should render correctly', () => { + const { getByRole } = render(); + expect(getByRole('textbox')).toBeDefined(); + }); + it('should render and be able to input text', () => { + const { getByTestId } = render( + , + ); + const textFieldBase = getByTestId('text-field-base'); + + expect(textFieldBase.value).toBe(''); // initial value is empty string + fireEvent.change(textFieldBase, { target: { value: 'text value' } }); + expect(textFieldBase.value).toBe('text value'); + fireEvent.change(textFieldBase, { target: { value: '' } }); // reset value + expect(textFieldBase.value).toBe(''); // value is empty string after reset + }); + it('should render and fire onFocus and onBlur events', () => { + const onFocus = jest.fn(); + const onBlur = jest.fn(); + const { getByTestId } = render( + , + ); + const textFieldBase = getByTestId('text-field-base'); + + fireEvent.focus(textFieldBase); + expect(onFocus).toHaveBeenCalledTimes(1); + fireEvent.blur(textFieldBase); + expect(onBlur).toHaveBeenCalledTimes(1); + }); + it('should render and fire onChange event', () => { + const onChange = jest.fn(); + const { getByTestId } = render( + , + ); + const textFieldBase = getByTestId('text-field-base'); + + fireEvent.change(textFieldBase, { target: { value: 'text value' } }); + expect(onChange).toHaveBeenCalledTimes(1); + }); + it('should render and fire onClick event', () => { + const onClick = jest.fn(); + const { getByTestId } = render( + , + ); + const textFieldBase = getByTestId('text-field-base'); + + fireEvent.click(textFieldBase); + expect(onClick).toHaveBeenCalledTimes(1); + }); + it('should render with different size classes', () => { + const { getByTestId } = render( + <> + + + + , + ); + expect(getByTestId('sm')).toHaveClass('mm-text-field-base--size-sm'); + expect(getByTestId('md')).toHaveClass('mm-text-field-base--size-md'); + expect(getByTestId('lg')).toHaveClass('mm-text-field-base--size-lg'); + }); + it('should render with different types', () => { + const { getByTestId } = render( + <> + + + + , + ); + expect(getByTestId('text-field-base-text')).toHaveAttribute('type', 'text'); + expect(getByTestId('text-field-base-number')).toHaveAttribute( + 'type', + 'number', + ); + expect(getByTestId('text-field-base-password')).toHaveAttribute( + 'type', + 'password', + ); + }); + it('should render with truncate class', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('truncate')).toHaveClass('mm-text-field-base--truncate'); + }); + it('should render with right and left accessories', () => { + const { getByRole, getByText } = render( + left accessory} + rightAccessory={
right accessory
} + />, + ); + expect(getByRole('textbox')).toBeDefined(); + expect(getByText('left accessory')).toBeDefined(); + expect(getByText('right accessory')).toBeDefined(); + }); + it('should render with working ref using inputRef prop', () => { + // Because the 'ref' attribute wont flow down to the DOM + // I'm not exactly sure how to test this? + const mockRef = jest.fn(); + const { getByRole } = render(); + expect(getByRole('textbox')).toBeDefined(); + expect(mockRef).toHaveBeenCalledTimes(1); + }); + it('should render with autoComplete', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('text-field-base-auto-complete')).toHaveAttribute( + 'autocomplete', + 'on', + ); + }); + it('should render with autoFocus', () => { + const { getByRole } = render(); + expect(getByRole('textbox')).toHaveFocus(); + }); + it('should render with a defaultValue', () => { + const { getByRole } = render( + , + ); + expect(getByRole('textbox').value).toBe('default value'); + }); + it('should render in disabled state and not focus or be clickable', () => { + const mockOnClick = jest.fn(); + const mockOnFocus = jest.fn(); + const { getByRole } = render( + , + ); + + getByRole('textbox').focus(); + expect(getByRole('textbox')).toBeDisabled(); + expect(mockOnClick).toHaveBeenCalledTimes(0); + expect(mockOnFocus).toHaveBeenCalledTimes(0); + }); + it('should render with error className when error is true', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('text-field-base-error')).toHaveClass( + 'mm-text-field-base--error', + ); + }); + it('should render with maxLength and not allow more than the set characters', async () => { + const { getByRole } = render(); + const textFieldBase = getByRole('textbox'); + await userEvent.type(textFieldBase, '1234567890'); + expect(getByRole('textbox')).toBeDefined(); + expect(textFieldBase.maxLength).toBe(5); + expect(textFieldBase.value).toBe('12345'); + expect(textFieldBase.value).toHaveLength(5); + }); + it('should render with readOnly attr when readOnly is true', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('text-field-base-readonly')).toHaveAttribute( + 'readonly', + '', + ); + }); + it('should render with required attr when required is true', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('text-field-base-required')).toHaveAttribute( + 'required', + '', + ); + }); +}); From db59186ced8f1a0b98587c3207201a528c1e3757 Mon Sep 17 00:00:00 2001 From: George Marshall Date: Thu, 6 Oct 2022 12:42:52 -0700 Subject: [PATCH 4/5] Adding ast-types to resolutions (#16103) --- package.json | 3 ++- yarn.lock | 7 +------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 0a64aad1e9a2..4ba4375ac381 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,8 @@ "pubnub/superagent-proxy": "^3.0.0", "pull-ws": "^3.3.2", "json-schema": "^0.4.0", - "simple-get": "^4.0.1" + "simple-get": "^4.0.1", + "@storybook/**/ast-types": "^0.14.2" }, "dependencies": { "3box": "^1.10.2", diff --git a/yarn.lock b/yarn.lock index 08a73a300e84..7403c438b107 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7129,12 +7129,7 @@ ast-module-types@^2.3.2, ast-module-types@^2.4.0, ast-module-types@^2.7.0, ast-m resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-2.7.1.tgz#3f7989ef8dfa1fdb82dfe0ab02bdfc7c77a57dd3" integrity sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw== -ast-types@^0.13.2: - version "0.13.2" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" - integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== - -ast-types@^0.14.2: +ast-types@^0.13.2, ast-types@^0.14.2: version "0.14.2" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== From 7ba0f78a84c2c3a9bbd17b9d921a0700fafa0750 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 7 Oct 2022 10:02:35 +0200 Subject: [PATCH 5/5] [FLASK] `snaps-skunkworks@0.22.0` (#16069) * snaps-skunkworks@0.22.0 * Update LavaMoat policies * Bump execution environment and fix a breaking change * Fix caveat and permissions * Fix test * Exclude keyring endowment for now * Fix test * Fix snap_confirm missing title --- .../permissions/flask/snap-permissions.js | 9 +- .../controllers/permissions/specifications.js | 2 + .../permissions/specifications.test.js | 5 +- app/scripts/metamask-controller.js | 4 +- lavamoat/browserify/beta/policy.json | 28 ++---- lavamoat/browserify/flask/policy.json | 52 +++++------ lavamoat/browserify/main/policy.json | 28 ++---- package.json | 6 +- shared/constants/permissions.test.js | 5 +- shared/constants/permissions.ts | 3 +- .../flask/snap-confirm/snap-confirm.js | 4 +- yarn.lock | 87 ++++++++++--------- 12 files changed, 113 insertions(+), 120 deletions(-) diff --git a/app/scripts/controllers/permissions/flask/snap-permissions.js b/app/scripts/controllers/permissions/flask/snap-permissions.js index 07024c9ae19f..1a1d78c5d2d3 100644 --- a/app/scripts/controllers/permissions/flask/snap-permissions.js +++ b/app/scripts/controllers/permissions/flask/snap-permissions.js @@ -3,7 +3,10 @@ import { restrictedMethodPermissionBuilders, selectHooks, } from '@metamask/rpc-methods'; -import { ExcludedSnapPermissions } from '../../../../../shared/constants/permissions'; +import { + ExcludedSnapEndowments, + ExcludedSnapPermissions, +} from '../../../../../shared/constants/permissions'; /** * @returns {Record>} All endowment permission @@ -12,7 +15,9 @@ import { ExcludedSnapPermissions } from '../../../../../shared/constants/permiss export const buildSnapEndowmentSpecifications = () => Object.values(endowmentPermissionBuilders).reduce( (allSpecifications, { targetKey, specificationBuilder }) => { - allSpecifications[targetKey] = specificationBuilder(); + if (!ExcludedSnapEndowments.has(targetKey)) { + allSpecifications[targetKey] = specificationBuilder(); + } return allSpecifications; }, {}, diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index de6b1bdad89d..1e3fad4a4829 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -1,5 +1,6 @@ import { constructPermission, PermissionType } from '@metamask/controllers'; ///: BEGIN:ONLY_INCLUDE_IN(flask) +import { endowmentCaveatSpecifications as snapsEndowmentCaveatSpecifications } from '@metamask/snap-controllers'; import { caveatSpecifications as snapsCaveatsSpecifications } from '@metamask/rpc-methods'; ///: END:ONLY_INCLUDE_IN import { @@ -69,6 +70,7 @@ export const getCaveatSpecifications = ({ getIdentities }) => { ///: BEGIN:ONLY_INCLUDE_IN(flask) ...snapsCaveatsSpecifications, + ...snapsEndowmentCaveatSpecifications, ///: END:ONLY_INCLUDE_IN }; }; diff --git a/app/scripts/controllers/permissions/specifications.test.js b/app/scripts/controllers/permissions/specifications.test.js index e874d3aa6107..4d313b12ceb0 100644 --- a/app/scripts/controllers/permissions/specifications.test.js +++ b/app/scripts/controllers/permissions/specifications.test.js @@ -16,7 +16,7 @@ describe('PermissionController specifications', () => { describe('caveat specifications', () => { it('getCaveatSpecifications returns the expected specifications object', () => { const caveatSpecifications = getCaveatSpecifications({}); - expect(Object.keys(caveatSpecifications)).toHaveLength(3); + expect(Object.keys(caveatSpecifications)).toHaveLength(4); expect( caveatSpecifications[CaveatTypes.restrictReturnedAccounts].type, ).toStrictEqual(CaveatTypes.restrictReturnedAccounts); @@ -27,6 +27,9 @@ describe('PermissionController specifications', () => { expect(caveatSpecifications.permittedCoinTypes.type).toStrictEqual( SnapCaveatType.PermittedCoinTypes, ); + expect(caveatSpecifications.snapKeyring.type).toStrictEqual( + SnapCaveatType.SnapKeyring, + ); }); describe('restrictReturnedAccounts', () => { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 9d24755f7b1d..51b753ac2179 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -654,7 +654,7 @@ export default class MetamaskController extends EventEmitter { ///: BEGIN:ONLY_INCLUDE_IN(flask) this.snapExecutionService = new IframeExecutionService({ iframeUrl: new URL( - 'https://metamask.github.io/iframe-execution-environment/0.8.0', + 'https://metamask.github.io/iframe-execution-environment/0.9.0', ), messenger: this.controllerMessenger.getRestricted({ name: 'ExecutionService', @@ -3744,7 +3744,7 @@ export default class MetamaskController extends EventEmitter { ), getSnaps: this.controllerMessenger.call.bind( this.controllerMessenger, - 'SnapController:getSnaps', + 'SnapController:getPermittedSnaps', origin, ), requestPermissions: async (requestedPermissions) => { diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 2e1e455bc59c..3f8260c5eafb 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -682,11 +682,11 @@ "3box>ipfs>ipfs-unixfs-importer>async-iterator-batch": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-first": true, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": true, + "3box>ipfs>ipfs-unixfs-importer>superstruct": true, "3box>ipfs>ipld-dag-pb": true, "3box>ipfs>ipld-raw>multihashing-async": true, "3box>ipfs>multicodec": true, "3box>ipfs>multihashes": true, - "3box>ipfs>superstruct": true, "browserify>buffer": true, "madge>rc>deep-extend": true } @@ -841,11 +841,11 @@ "3box>ipfs>libp2p>libp2p-floodsub": true, "3box>ipfs>libp2p>libp2p-ping": true, "3box>ipfs>libp2p>libp2p-switch": true, + "3box>ipfs>libp2p>superstruct": true, "3box>ipfs>multiaddr": true, "3box>ipfs>peer-book": true, "3box>ipfs>peer-id": true, "3box>ipfs>peer-info": true, - "3box>ipfs>superstruct": true, "browserify>events": true, "browserify>insert-module-globals>is-buffer": true, "browserify>process": true, @@ -3068,20 +3068,13 @@ "setTimeout": true }, "packages": { - "@metamask/eth-json-rpc-infura>@metamask/utils": true, "@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": true, + "eth-block-tracker>@metamask/utils": true, "eth-rpc-errors": true, "json-rpc-engine": true, "node-fetch": true } }, - "@metamask/eth-json-rpc-infura>@metamask/utils": { - "packages": { - "@metamask/eth-json-rpc-infura>@metamask/utils>superstruct": true, - "eslint>fast-deep-equal": true, - "nock>debug": true - } - }, "@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": { "globals": { "URL": true, @@ -4494,9 +4487,13 @@ } }, "eth-block-tracker>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, "packages": { + "@metamask/snap-utils>superstruct": true, "eslint>fast-deep-equal": true, - "eth-block-tracker>@metamask/utils>superstruct": true, "nock>debug": true } }, @@ -4553,7 +4550,7 @@ }, "packages": { "browserify>browser-resolve": true, - "eth-json-rpc-middleware>@metamask/utils": true, + "eth-block-tracker>@metamask/utils": true, "eth-json-rpc-middleware>eth-sig-util": true, "eth-json-rpc-middleware>pify": true, "eth-rpc-errors": true, @@ -4563,13 +4560,6 @@ "vinyl>clone": true } }, - "eth-json-rpc-middleware>@metamask/utils": { - "packages": { - "eslint>fast-deep-equal": true, - "eth-json-rpc-middleware>@metamask/utils>superstruct": true, - "nock>debug": true - } - }, "eth-json-rpc-middleware>eth-sig-util": { "packages": { "eth-json-rpc-middleware>eth-sig-util>ethereumjs-abi": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 6ba23b6c3f72..ea5ae2873364 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -682,11 +682,11 @@ "3box>ipfs>ipfs-unixfs-importer>async-iterator-batch": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-first": true, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": true, + "3box>ipfs>ipfs-unixfs-importer>superstruct": true, "3box>ipfs>ipld-dag-pb": true, "3box>ipfs>ipld-raw>multihashing-async": true, "3box>ipfs>multicodec": true, "3box>ipfs>multihashes": true, - "3box>ipfs>superstruct": true, "browserify>buffer": true, "madge>rc>deep-extend": true } @@ -841,11 +841,11 @@ "3box>ipfs>libp2p>libp2p-floodsub": true, "3box>ipfs>libp2p>libp2p-ping": true, "3box>ipfs>libp2p>libp2p-switch": true, + "3box>ipfs>libp2p>superstruct": true, "3box>ipfs>multiaddr": true, "3box>ipfs>peer-book": true, "3box>ipfs>peer-id": true, "3box>ipfs>peer-info": true, - "3box>ipfs>superstruct": true, "browserify>events": true, "browserify>insert-module-globals>is-buffer": true, "browserify>process": true, @@ -3229,20 +3229,13 @@ "setTimeout": true }, "packages": { - "@metamask/eth-json-rpc-infura>@metamask/utils": true, "@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": true, + "eth-block-tracker>@metamask/utils": true, "eth-rpc-errors": true, "json-rpc-engine": true, "node-fetch": true } }, - "@metamask/eth-json-rpc-infura>@metamask/utils": { - "packages": { - "@metamask/eth-json-rpc-infura>@metamask/utils>superstruct": true, - "eslint>fast-deep-equal": true, - "nock>debug": true - } - }, "@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": { "globals": { "URL": true, @@ -3552,11 +3545,15 @@ } }, "@metamask/rpc-methods": { + "globals": { + "console.warn": true + }, "packages": { "@metamask/rpc-methods>@metamask/controllers": true, "@metamask/rpc-methods>@metamask/key-tree": true, "@metamask/snap-utils": true, - "@metamask/snap-utils>@metamask/utils": true, + "@metamask/snap-utils>superstruct": true, + "eth-block-tracker>@metamask/utils": true, "eth-rpc-errors": true } }, @@ -3869,7 +3866,7 @@ "@metamask/snap-controllers>readable-web-to-node-stream": true, "@metamask/snap-controllers>tar-stream": true, "@metamask/snap-utils": true, - "@metamask/snap-utils>@metamask/utils": true, + "eth-block-tracker>@metamask/utils": true, "eth-rpc-errors": true, "json-rpc-engine": true, "pump": true @@ -4034,8 +4031,13 @@ "removeEventListener": true }, "packages": { - "@metamask/snap-controllers>@metamask/post-message-stream>readable-stream": true, - "@metamask/snap-utils>@metamask/utils": true + "@metamask/snap-controllers>@metamask/post-message-stream>@metamask/utils": true, + "@metamask/snap-controllers>@metamask/post-message-stream>readable-stream": true + } + }, + "@metamask/snap-controllers>@metamask/post-message-stream>@metamask/utils": { + "packages": { + "eslint>fast-deep-equal": true } }, "@metamask/snap-controllers>@metamask/post-message-stream>readable-stream": { @@ -4259,20 +4261,17 @@ "@babel/core>@babel/types": true, "@metamask/snap-utils>ajv": true, "@metamask/snap-utils>rfdc": true, + "@metamask/snap-utils>superstruct": true, "browserify": true, "browserify>buffer": true, "browserify>crypto-browserify": true, "browserify>events": true, "browserify>path-browserify": true, "eslint>fast-deep-equal": true, + "eth-block-tracker>@metamask/utils": true, "semver": true } }, - "@metamask/snap-utils>@metamask/utils": { - "packages": { - "eslint>fast-deep-equal": true - } - }, "@metamask/snap-utils>rfdc": { "packages": { "browserify>buffer": true @@ -5314,9 +5313,13 @@ } }, "eth-block-tracker>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, "packages": { + "@metamask/snap-utils>superstruct": true, "eslint>fast-deep-equal": true, - "eth-block-tracker>@metamask/utils>superstruct": true, "nock>debug": true } }, @@ -5373,7 +5376,7 @@ }, "packages": { "browserify>browser-resolve": true, - "eth-json-rpc-middleware>@metamask/utils": true, + "eth-block-tracker>@metamask/utils": true, "eth-json-rpc-middleware>eth-sig-util": true, "eth-json-rpc-middleware>pify": true, "eth-rpc-errors": true, @@ -5383,13 +5386,6 @@ "vinyl>clone": true } }, - "eth-json-rpc-middleware>@metamask/utils": { - "packages": { - "eslint>fast-deep-equal": true, - "eth-json-rpc-middleware>@metamask/utils>superstruct": true, - "nock>debug": true - } - }, "eth-json-rpc-middleware>eth-sig-util": { "packages": { "eth-json-rpc-middleware>eth-sig-util>ethereumjs-abi": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 2e1e455bc59c..3f8260c5eafb 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -682,11 +682,11 @@ "3box>ipfs>ipfs-unixfs-importer>async-iterator-batch": true, "3box>ipfs>ipfs-unixfs-importer>async-iterator-first": true, "3box>ipfs>ipfs-unixfs-importer>rabin-wasm": true, + "3box>ipfs>ipfs-unixfs-importer>superstruct": true, "3box>ipfs>ipld-dag-pb": true, "3box>ipfs>ipld-raw>multihashing-async": true, "3box>ipfs>multicodec": true, "3box>ipfs>multihashes": true, - "3box>ipfs>superstruct": true, "browserify>buffer": true, "madge>rc>deep-extend": true } @@ -841,11 +841,11 @@ "3box>ipfs>libp2p>libp2p-floodsub": true, "3box>ipfs>libp2p>libp2p-ping": true, "3box>ipfs>libp2p>libp2p-switch": true, + "3box>ipfs>libp2p>superstruct": true, "3box>ipfs>multiaddr": true, "3box>ipfs>peer-book": true, "3box>ipfs>peer-id": true, "3box>ipfs>peer-info": true, - "3box>ipfs>superstruct": true, "browserify>events": true, "browserify>insert-module-globals>is-buffer": true, "browserify>process": true, @@ -3068,20 +3068,13 @@ "setTimeout": true }, "packages": { - "@metamask/eth-json-rpc-infura>@metamask/utils": true, "@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": true, + "eth-block-tracker>@metamask/utils": true, "eth-rpc-errors": true, "json-rpc-engine": true, "node-fetch": true } }, - "@metamask/eth-json-rpc-infura>@metamask/utils": { - "packages": { - "@metamask/eth-json-rpc-infura>@metamask/utils>superstruct": true, - "eslint>fast-deep-equal": true, - "nock>debug": true - } - }, "@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": { "globals": { "URL": true, @@ -4494,9 +4487,13 @@ } }, "eth-block-tracker>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, "packages": { + "@metamask/snap-utils>superstruct": true, "eslint>fast-deep-equal": true, - "eth-block-tracker>@metamask/utils>superstruct": true, "nock>debug": true } }, @@ -4553,7 +4550,7 @@ }, "packages": { "browserify>browser-resolve": true, - "eth-json-rpc-middleware>@metamask/utils": true, + "eth-block-tracker>@metamask/utils": true, "eth-json-rpc-middleware>eth-sig-util": true, "eth-json-rpc-middleware>pify": true, "eth-rpc-errors": true, @@ -4563,13 +4560,6 @@ "vinyl>clone": true } }, - "eth-json-rpc-middleware>@metamask/utils": { - "packages": { - "eslint>fast-deep-equal": true, - "eth-json-rpc-middleware>@metamask/utils>superstruct": true, - "nock>debug": true - } - }, "eth-json-rpc-middleware>eth-sig-util": { "packages": { "eth-json-rpc-middleware>eth-sig-util>ethereumjs-abi": true, diff --git a/package.json b/package.json index 4ba4375ac381..6be08da738d6 100644 --- a/package.json +++ b/package.json @@ -135,11 +135,11 @@ "@metamask/obs-store": "^5.0.0", "@metamask/post-message-stream": "^4.0.0", "@metamask/providers": "^9.0.0", - "@metamask/rpc-methods": "^0.21.0", + "@metamask/rpc-methods": "^0.22.0", "@metamask/slip44": "^2.1.0", "@metamask/smart-transactions-controller": "^2.3.2", - "@metamask/snap-controllers": "^0.21.0", - "@metamask/snap-utils": "^0.21.0", + "@metamask/snap-controllers": "^0.22.0", + "@metamask/snap-utils": "^0.22.0", "@ngraveio/bc-ur": "^1.1.6", "@popperjs/core": "^2.4.0", "@reduxjs/toolkit": "^1.6.2", diff --git a/shared/constants/permissions.test.js b/shared/constants/permissions.test.js index 3637fed5818a..433d6629c3b5 100644 --- a/shared/constants/permissions.test.js +++ b/shared/constants/permissions.test.js @@ -2,6 +2,7 @@ import { endowmentPermissionBuilders } from '@metamask/snap-controllers'; import { restrictedMethodPermissionBuilders } from '@metamask/rpc-methods'; import { EndowmentPermissions, + ExcludedSnapEndowments, ExcludedSnapPermissions, RestrictedMethods, } from './permissions'; @@ -9,7 +10,9 @@ import { describe('EndowmentPermissions', () => { it('has the expected permission keys', () => { expect(Object.keys(EndowmentPermissions).sort()).toStrictEqual( - Object.keys(endowmentPermissionBuilders).sort(), + Object.keys(endowmentPermissionBuilders) + .filter((targetKey) => !ExcludedSnapEndowments.has(targetKey)) + .sort(), ); }); }); diff --git a/shared/constants/permissions.ts b/shared/constants/permissions.ts index ced11127fe44..49c281f1355e 100644 --- a/shared/constants/permissions.ts +++ b/shared/constants/permissions.ts @@ -27,5 +27,6 @@ export const EndowmentPermissions = Object.freeze({ } as const); // Methods / permissions in external packages that we are temporarily excluding. -export const ExcludedSnapPermissions = new Set([]); +export const ExcludedSnapPermissions = new Set(['snap_dialog']); +export const ExcludedSnapEndowments = new Set(['endowment:keyring']); ///: END:ONLY_INCLUDE_IN diff --git a/ui/pages/confirmation/templates/flask/snap-confirm/snap-confirm.js b/ui/pages/confirmation/templates/flask/snap-confirm/snap-confirm.js index a5d278450a57..120bea07cdd2 100644 --- a/ui/pages/confirmation/templates/flask/snap-confirm/snap-confirm.js +++ b/ui/pages/confirmation/templates/flask/snap-confirm/snap-confirm.js @@ -5,14 +5,14 @@ import { import ZENDESK_URLS from '../../../../../helpers/constants/zendesk-url'; function getValues(pendingApproval, t, actions) { - const { prompt, description, textAreaContent } = pendingApproval.requestData; + const { title, description, textAreaContent } = pendingApproval.requestData; return { content: [ { element: 'Typography', key: 'title', - children: prompt, + children: title, props: { variant: TYPOGRAPHY.H3, align: 'center', diff --git a/yarn.lock b/yarn.lock index 7403c438b107..e2271e8d68a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3317,17 +3317,17 @@ resolved "https://registry.yarnpkg.com/@metamask/etherscan-link/-/etherscan-link-2.2.0.tgz#76314d0c1405a0669fc4a0a19e0877bd3d0c389f" integrity sha512-xUgehvgU+ZbzeJ44m4sUtsyf6Dwou+SlYhiKfi6lkRcbWh6Jl3TCi0YM9C7XWgxfnLSdQBO1ndvcp0kslKgMsA== -"@metamask/execution-environments@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@metamask/execution-environments/-/execution-environments-0.21.0.tgz#40d5c63215fb98a43f9d7b3b31c371dbed93adba" - integrity sha512-KnQ07SRcht/jpHa4KLHftI034GPZXzz2I1RsWk/0OMxQ3ECGJwDroH92pXNRy0+HTdz31XWhMzdc1GW5rR2CzA== +"@metamask/execution-environments@^0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@metamask/execution-environments/-/execution-environments-0.22.0.tgz#2dd67add467e64fc6e992cb36461f20959ed87fd" + integrity sha512-mb5O3HX5Nrt0urAPWohiyB9w465Qk12tF1hXz+vjwFZe4LAbTHza7XsbvTzAy1XD99JjhtLMi+IwaHp8GELcDg== dependencies: "@metamask/object-multiplex" "^1.2.0" "@metamask/post-message-stream" "^6.0.0" "@metamask/providers" "^9.0.0" - "@metamask/snap-types" "^0.21.0" - "@metamask/snap-utils" "^0.21.0" - "@metamask/utils" "^2.0.0" + "@metamask/snap-types" "^0.22.0" + "@metamask/snap-utils" "^0.22.0" + "@metamask/utils" "^3.1.0" eth-rpc-errors "^4.0.3" pump "^3.0.0" ses "^0.15.15" @@ -3449,17 +3449,18 @@ pump "^3.0.0" webextension-polyfill-ts "^0.25.0" -"@metamask/rpc-methods@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@metamask/rpc-methods/-/rpc-methods-0.21.0.tgz#7d00029aeaa606d5780fb2e7c01069e714822ae4" - integrity sha512-CXYBzIf6hYH8bkqkvGqFHFmsVdjYGznkgw96NpGosQS4vns07wrvBluA45WehAqgHDZHpom1ffQ8r11PksM86Q== +"@metamask/rpc-methods@^0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@metamask/rpc-methods/-/rpc-methods-0.22.0.tgz#17ee8db33deba5152d531123139fd3ea651e49ef" + integrity sha512-FCVbf68zgHHThG2OAOp9u7iBO1hjIMjo8S85bXsGtStUp9KjiGxnuQhyNK7b/Ny+WuHI+GP26abXRIyS2ampfA== dependencies: "@metamask/controllers" "^31.0.0" "@metamask/key-tree" "^5.0.2" - "@metamask/snap-utils" "^0.21.0" + "@metamask/snap-utils" "^0.22.0" "@metamask/types" "^1.1.0" - "@metamask/utils" "^2.1.0" + "@metamask/utils" "^3.1.0" eth-rpc-errors "^4.0.2" + superstruct "^0.16.5" "@metamask/safe-event-emitter@^2.0.0": version "2.0.0" @@ -3486,19 +3487,20 @@ isomorphic-fetch "^3.0.0" lodash "^4.17.21" -"@metamask/snap-controllers@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@metamask/snap-controllers/-/snap-controllers-0.21.0.tgz#5fdc2f350c8739b070f1bfdbc9c7ef7409af234d" - integrity sha512-WZyfB+mQMifB7a5tf5WEVrWnLdoSMG/sXL6YQnItC/96SUX//c9IW+iNB6ZZfiP5lJFwafN5hjOExWH3O/fwbw== +"@metamask/snap-controllers@^0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@metamask/snap-controllers/-/snap-controllers-0.22.0.tgz#bb1dfb7551cfca74b17addad2390ad80a6df8b7a" + integrity sha512-k7TgYyzZna54ns3o9GjnLiHJDT3xvkG4i+zSEJSMPzqAy5Su5p2yMiTe/awEwTVFBMsC9xtX9owc0lArkk8NZw== dependencies: "@metamask/browser-passworder" "^3.0.0" "@metamask/controllers" "^31.0.0" - "@metamask/execution-environments" "^0.21.0" + "@metamask/execution-environments" "^0.22.0" "@metamask/object-multiplex" "^1.1.0" "@metamask/post-message-stream" "^6.0.0" - "@metamask/rpc-methods" "^0.21.0" - "@metamask/snap-utils" "^0.21.0" - "@metamask/utils" "^2.0.0" + "@metamask/rpc-methods" "^0.22.0" + "@metamask/snap-types" "^0.22.0" + "@metamask/snap-utils" "^0.22.0" + "@metamask/utils" "^3.1.0" "@xstate/fsm" "^2.0.0" concat-stream "^2.0.0" eth-rpc-errors "^4.0.2" @@ -3511,29 +3513,30 @@ readable-web-to-node-stream "^3.0.2" tar-stream "^2.2.0" -"@metamask/snap-types@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@metamask/snap-types/-/snap-types-0.21.0.tgz#51380240a97a6ef492dca9d5bcb2d7c6f6c8a023" - integrity sha512-wyamdpiZqVfrHjAysnplVQexEisH8b00XTv6F8kTyKd4pbRTBswmOOc1DhBbi2+7Fw2J5a7pdLb9+/MRMwnztg== +"@metamask/snap-types@^0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@metamask/snap-types/-/snap-types-0.22.0.tgz#4d74e34eea69a7afe0ad9a4d54be83cf5391bf9e" + integrity sha512-XwJNk+Qbtr7xjuh1Iz7wqybp+xB3H6uRJjlOqtgIoOui9V2zfL/At/FzHMMvaLeUsWJhOVSJP5qmEzCYaOEvmQ== dependencies: "@metamask/providers" "^9.0.0" - "@metamask/snap-utils" "^0.21.0" + "@metamask/snap-utils" "^0.22.0" "@metamask/types" "^1.1.0" -"@metamask/snap-utils@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@metamask/snap-utils/-/snap-utils-0.21.0.tgz#7ed5df1d0c25f2660ce36dcc436f3608d72e92d5" - integrity sha512-bfGvCIZd1zfuRUFN68F0HrIHfsCmoh/j6/Sm5sj6v7mIGyERbgpu0mYEwKIRjuCtV5GkGimXU6H2Tof+ZWrLtQ== +"@metamask/snap-utils@^0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@metamask/snap-utils/-/snap-utils-0.22.0.tgz#1815eecfc8a388a4ae6744664ff28f58404730ee" + integrity sha512-mwe72I/sVMVogNU+gxUU1P2Kos4S/4lEX1MUz7Hr79jfYzTgwTfxz/gaMubjD6CF2q0ZvF5JQr6OBk6ZRLHhCw== dependencies: "@babel/core" "^7.18.6" - "@metamask/snap-types" "^0.21.0" - "@metamask/utils" "^2.0.0" + "@metamask/snap-types" "^0.22.0" + "@metamask/utils" "^3.1.0" ajv "^8.11.0" eth-rpc-errors "^4.0.3" fast-deep-equal "^3.1.3" rfdc "^1.3.0" semver "^7.3.7" ses "^0.15.17" + superstruct "^0.16.5" "@metamask/test-dapp@^5.2.1": version "5.2.1" @@ -3545,22 +3548,22 @@ resolved "https://registry.yarnpkg.com/@metamask/types/-/types-1.1.0.tgz#9bd14b33427932833c50c9187298804a18c2e025" integrity sha512-EEV/GjlYkOSfSPnYXfOosxa3TqYtIW3fhg6jdw+cok/OhMgNn4wCfbENFqjytrHMU2f7ZKtBAvtiP5V8H44sSw== -"@metamask/utils@^2.0.0", "@metamask/utils@^2.1.0": +"@metamask/utils@^2.0.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-2.1.0.tgz#a65eaa0932b863383844ec323e05e293d8e718ab" integrity sha512-4PHdo5B1ifpw6GbsdlDpp8oqA++rddSmt2pWBHtIGGL2tQMvmfHdaDDSns4JP9iC+AbMogVcUpv5Vt8ow1zsRA== dependencies: fast-deep-equal "^3.1.3" -"@metamask/utils@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-3.0.1.tgz#53ed2b9cbbd12b18c1db6929728446633d6a74c4" - integrity sha512-FDWQ+MeeWQh0b/w2D50+oVPrRqUIvawDlPia+EH9NCOFN0Ty3KkzkQXfO6FgHKMyR4aWuS4SBOrhWFRW8km6lQ== +"@metamask/utils@^3.0.1", "@metamask/utils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-3.1.0.tgz#ecfabe08e807bfcfb9ed1d7e727779a9382bee2c" + integrity sha512-zBGKgaqdKO9z2CoBSDzeE5KJUr5pM72YsumyUiklSyqMg/xL9vu7Z+E/pkRtwPmyi2YWXvq1rWfjugTt2+38nA== dependencies: "@types/debug" "^4.1.7" debug "^4.3.4" fast-deep-equal "^3.1.3" - superstruct "^0.16.0" + superstruct "^0.16.5" "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" @@ -26687,10 +26690,10 @@ superagent@^3.8.1: qs "^6.5.1" readable-stream "^2.3.5" -superstruct@^0.16.0: - version "0.16.0" - resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.16.0.tgz#9af5e059acd08e774789ad8880962427ef68dace" - integrity sha512-IDQtwnnlaan1NhuHqyD/U11lROYvCQ79JyfwlFU9xEVHzqV/Ys/RrwmHPCG0CVH/1g0BuodEjH1msxK2UHxehA== +superstruct@^0.16.5: + version "0.16.5" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.16.5.tgz#7b7e1f1f8bf6ab141c660e501ac57026e42c09c0" + integrity sha512-GBa1VPdCUDAIrsoMVy2lzE/hKQnieUlc1JVoVzJ2YLx47SoPY4AqF85Ht1bPg5r+8I0v54GbaRdNTnYQ0p+T+Q== superstruct@^0.6.0, superstruct@~0.6.0, superstruct@~0.6.1: version "0.6.1"