Skip to content

feat: SensitiveText extension#1164

Merged
kirillzyusko merged 3 commits into
mainfrom
feat/sensitive-text-extension
May 13, 2026
Merged

feat: SensitiveText extension#1164
kirillzyusko merged 3 commits into
mainfrom
feat/sensitive-text-extension

Conversation

@kirillzyusko

@kirillzyusko kirillzyusko commented May 7, 2026

Copy link
Copy Markdown
Collaborator

Description

Added SensitiveText to DSR.

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-312

Manual testing steps

  1. Open Storybook app
  2. Check SensitiveText component

Screenshots/Recordings

Before

Screenshot 2026-05-07 at 19 27 14

After

image

Pre-merge author checklist

  • I've followed MetaMask Contributor Docs
  • I've completed the PR template to the best of my ability
  • I’ve included tests if applicable
  • I’ve documented my code using JSDoc format if applicable
  • I’ve applied the right labels on the PR (see labeling guidelines). Not required for external contributors.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Low Risk
Mostly additive: introduces a new SensitiveText component for design-system-react and centralizes SensitiveTextLength/shared prop types in design-system-shared. Low risk aside from potential minor import/type churn for consumers relying on the old React Native-local constants/types.

Overview
Adds SensitiveText to @metamask/design-system-react (component, stories, docs, and tests) with the same hide-to-bullets behavior and length validation/warning logic.

Centralizes SensitiveTextLength and shared prop/type definitions in @metamask/design-system-shared, updates React Native SensitiveText to consume/re-export these shared constants/types, and documents the new import paths in the React migration guide.

Reviewed by Cursor Bugbot for commit c30fc8d. Bugbot is set up for automated code reviews on this repo. Configure here.

@kirillzyusko kirillzyusko self-assigned this May 7, 2026
@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@kirillzyusko kirillzyusko marked this pull request as ready for review May 7, 2026 17:30
@kirillzyusko kirillzyusko requested a review from a team as a code owner May 7, 2026 17:30
const fallback = useMemo(() => {
let resolvedLength = length;

if (!(length in SensitiveTextLength) && !isValidLength(length)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation checks object keys instead of values

Medium Severity

The length in SensitiveTextLength check uses the in operator, which tests whether length is a key of the object (e.g. 'Short'), not a value (e.g. '6'). Since the prop type is effectively string (because CustomLength = string subsumes the literal types), a consumer can pass length="Short" instead of length={SensitiveTextLength.Short} without any TypeScript error. In that case, the in check passes, skipping validation entirely, and Number('Short') produces NaN — silently rendering zero bullets instead of falling back with a warning. The in check adds no value for correct inputs either (all enum values like '6' are already handled by isValidLength), making it purely a source of silent failures.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ea7ef55. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the same check in react-native too, and I remember cursor was complaining about it too, but we decided to keep current codebase. Let's stick to this patter here as well 🤞

@kirillzyusko kirillzyusko force-pushed the feat/sensitive-text-extension branch from ea7ef55 to b2fc3cb Compare May 8, 2026 10:12
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@brianacnguyen brianacnguyen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you address Bugbot comments?

@kirillzyusko kirillzyusko enabled auto-merge (squash) May 13, 2026 09:11
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@kirillzyusko kirillzyusko merged commit dd5a110 into main May 13, 2026
44 checks passed
@kirillzyusko kirillzyusko deleted the feat/sensitive-text-extension branch May 13, 2026 09:15

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Reviewed by Cursor Bugbot for commit c30fc8d. Configure here.


const numLength = Number(resolvedLength);
return '•'.repeat(Number.isNaN(numLength) ? 0 : numLength);
}, [length]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verbatim duplicated platform-agnostic logic across web and RN

Medium Severity

The new web SensitiveText.tsx is a character-for-character copy of the RN implementation. The isValidLength helper and the entire useMemo validation/fallback computation are 100% platform-agnostic — no platform-specific API is used. design-system-shared already has a utils/ directory for exactly this kind of shared logic. Duplicating it means any future bug fix (e.g., fixing the in-check to compare values instead of keys) must be applied in two files independently, risking silent divergence.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c30fc8d. Configure here.

import type { Meta, StoryObj } from '@storybook/react-vite';
import React from 'react';

import { TextColor, TextVariant } from '../Text';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Story imports shared consts from sibling component

Low Severity

TextColor and TextVariant are imported from ../Text (a sibling component) instead of @metamask/design-system-shared. Both const objects are confirmed to exist in shared. Stories are consumer-facing examples, and newer stories in this repo (e.g., Icon.stories.tsx, Input.stories.tsx) already import these from shared. Using the shared import teaches consumers the canonical import path.

Fix in Cursor Fix in Web

Triggered by learned rule: Import shared types from @metamask/design-system-shared

Reviewed by Cursor Bugbot for commit c30fc8d. Configure here.

@brianacnguyen brianacnguyen mentioned this pull request May 15, 2026
17 tasks
brianacnguyen added a commit that referenced this pull request May 15, 2026
## Release 40.0.0

This release updates the shared layer and both UI packages: new
extension-aligned primitives and form helpers on web, new selection and
multi-line input components on React Native, shared types and context
for those APIs, coordinated **`BannerBase`** close behavior, and
**`ButtonBase`** defaults driven by **`size`** (with migration guidance
for wrappers that overrode label, icon, or spacing).

### Package versions

- `@metamask/design-system-shared`: **0.18.0**
- `@metamask/design-system-react`: **0.23.0**
- `@metamask/design-system-react-native`: **0.25.0**

### Shared type updates (0.18.0)

#### Component type additions
([#1172](#1172),
[#1164](#1164),
[#1169](#1169),
[#1141](#1141))

**What changed**

- **`SelectButton`** shared props and variants
(`SelectButtonPropsShared`, `SelectButtonVariant`,
`SelectButtonEndArrow`).
- **`SegmentGroup`** shared props and **`SegmentGroupContext`**
(`SegmentGroupPropsShared`, `SegmentGroupContext`,
`SegmentGroupContextValue`).
- **`SensitiveText`** shared types (`SensitiveTextLength`,
`SensitiveTextPropsShared`, and related exports).
- **`HelpText`** shared types (`HelpTextSeverity`,
`HelpTextPropsShared`, and related exports).
- **`TextAreaPropsShared`** for multi-line input wrappers.

**Impact**

- React and React Native stay aligned on the same ADR-0003/0004-style
contracts for the new and updated surfaces above.

### React web updates (0.23.0)

#### Added

- **`PopoverHeader`** — popover title rows and trailing actions,
extension migration patterns
([#1158](#1158)).
- **`ModalHeader`** — modal title rows and accessory slots
([#1144](#1144)).
- **`Label`** — captions paired with form controls
([#1152](#1152)).
- **`SensitiveText`** — mask/reveal for sensitive strings with
configurable visible length
([#1164](#1164)).
- **`HelpText`** — helper, success, warning, and error copy under inputs
and controls
([#1169](#1169)).

#### Changed

- **`ButtonBase`** — label **`Text`** variant, start/end icon sizes, and
internal spacing are derived from **`size`** for each
**`ButtonBaseSize`**
([#1150](#1150)).
See [Migration guide —
ButtonBase](https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react/MIGRATION.md#buttonbase-size-defaults).
- **`BannerBase`** — close behavior uses **`onClose`** only;
**`closeButtonProps.onClick`** is not the dismiss API;
**`closeButtonProps`** is for customization
([#1166](#1166)).

### React Native updates (0.25.0)

#### Added

- **`SelectButton`**, **`SegmentButton`**, **`SegmentGroup`**
([#1172](#1172)).
- **`SensitiveText`**, aligned with the shared contract
([#1164](#1164)).
- **`HeaderStandardAnimated`** and **`useHeaderStandardAnimated`**
([#1151](#1151)).
- **`TextArea`**
([#1141](#1141)).

#### Changed

- **`ButtonBase`** — same size-driven defaults as web
([#1150](#1150)).
See [Migration guide —
ButtonBase](https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/MIGRATION.md#buttonbase-size-defaults).
- **`BannerBase`** — close behavior uses **`onClose`** only;
**`closeButtonProps.onPress`** is not the dismiss API
([#1166](#1166)).

### Breaking changes

#### `BannerBase` close API (both platforms)

**What changed**

- **React:** Use **`onClose`** for dismiss behavior.
**`closeButtonProps.onClick`** is removed from that role. The close
control is tied to **`onClose`**. **`closeButtonProps`** remains for
non-behavioral customization (e.g. **`data-testid`**, accessibility,
styling hooks).
- **React Native:** Same pattern: **`onClose`** instead of
**`closeButtonProps.onPress`** for dismiss behavior.

**Migration**

- Move any close action from **`closeButtonProps.onClick`** /
**`closeButtonProps.onPress`** to **`onClose`**.
- If you previously forced a close button with only
**`closeButtonProps`**, also provide **`onClose`**.

**Impact**

- Call sites that dismissed via **`closeButtonProps`** or showed a close
button without **`onClose`** must be updated.

See:

- [React — From version 0.22.0 to
0.x.0](https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react/MIGRATION.md#from-version-0220-to-0x0)
- [React Native — From version 0.24.0 to
0.x.0](https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/MIGRATION.md#from-version-0240-to-0x0)

**`ButtonBase`:** not called out here as a semver-breaking API change;
consumers who overrode typography, icon size, or spacing on
**`ButtonBase`** wrappers should follow the ButtonBase migration
sections linked above.

### Checklist

- [x] Changelogs updated with human-readable descriptions
- [x] `yarn changelog:validate` passes
- [x] Version bumps follow semantic versioning
- [x] `@metamask/design-system-shared`: **minor** (0.17.x → 0.18.0) —
new shared exports for select/segment, sensitive text, help text, text
area
- [x] `@metamask/design-system-react`: **minor** (0.22.x → 0.23.0) — new
components; **`BannerBase`** / **`ButtonBase`** behavior
- [x] `@metamask/design-system-react-native`: **minor** (0.24.x →
0.25.0) — new components; **`BannerBase`** / **`ButtonBase`** behavior
- [x] Breaking changes documented (**`BannerBase`**) in MIGRATION with
before/after examples
- [x] PR references present in changelog entries

## Pre-merge author checklist

- [ ] [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [ ] [Release
Workflow](https://github.com/MetaMask/metamask-design-system/blob/main/.cursor/rules/release-workflow.md)
- [ ] `yarn build && yarn test && yarn lint`
- [ ] `yarn changelog:validate`

## Pre-merge reviewer checklist

- [ ] [Reviewing Release
PRs](https://github.com/MetaMask/metamask-design-system/blob/main/docs/reviewing-release-prs.md)
- [ ] Package versions and semver match what this PR actually publishes
- [ ] Changelogs are consumer-facing
- [ ] Breaking changes covered in MIGRATION.md
- [ ] No unpublished package is missing from the changelogs / version
list for **this** PR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants