メールアドレス変更ブロック
ChangeEmail は MUI 上に構築された制御済みのメールアドレス変更フォームブロックです。
インストール
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-change-email-block
yarn add @nodeblocks/frontend-change-email-block
pnpm add @nodeblocks/frontend-change-email-block
bun add @nodeblocks/frontend-change-email-block
必要なもの
| 項目 | 重要な理由 |
|---|---|
data | フォーム state の単一の信頼できる情報源 (newEmail) |
onDataChange | フィールド変更時に更新済み state とメタデータを受け取る |
currentEmail (optional) | 新しいメールアドレス欄の上に表示される読み取り専用の現在のアドレス |
errors (optional) | フィールド単位のエラーフィードバックを表示する |
制御されたコンポーネント
ChangeEmail はフォーム state を所有しません。state はアプリ側で保持し、data を通じて渡してください。
コード例
- Quick Start
- Labels and URLs
- Compound Components
- Block Override
ライブエディター
function Example() { const defaultData = {newEmail: ''}; const [data, setData] = React.useState(defaultData); const [errors, setErrors] = React.useState({}); const handleDataChange = (nextData, meta) => { const {[meta.name]: _removed, ...restErrors} = errors; let nextErrors = restErrors; // Validate required fields on blur (same pattern as storybook) if (meta.cause === 'blur' && meta.name === 'newEmail') { if (!String(nextData.newEmail || '').trim()) { nextErrors = {...restErrors, newEmail: 'New email is required.'}; } } setErrors(nextErrors); setData(nextData); }; return ( <ChangeEmail data={data} errors={Object.keys(errors).length ? errors : undefined} currentEmail="user@example.com" onDataChange={handleDataChange} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} /> ); }
結果
Loading...
現在のメールラベル、サブコンポーネントの文言、検証エラーをカスタマイズします。
ライブエディター
function Example() { const defaultData = {newEmail: 'not-an-email'}; const [data, setData] = React.useState(defaultData); return ( <ChangeEmail data={data} currentEmail="user@example.com" labels={{currentEmailDisplay: 'Current email'}} errors={{newEmail: 'Enter a valid email address.'}} onDataChange={setData} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} > <ChangeEmail.Title>Update your email</ChangeEmail.Title> <ChangeEmail.Form> <ChangeEmail.Form.CurrentEmailDisplay /> <ChangeEmail.Form.NewEmailField label="New email" placeholder="Enter your new email address" /> <ChangeEmail.Form.ChangeEmailButton>Update email</ChangeEmail.Form.ChangeEmailButton> </ChangeEmail.Form> </ChangeEmail> ); }
結果
Loading...
ラベルとフィールド文言
読み取り専用のメールラベルはルートで labels.currentEmailDisplay を渡してください。タイトル、新しいメール欄、ボタンの文言はサブコンポーネントの props で上書きします(Compound Components タブを参照)。デフォルトは、タイトル メールアドレスを変更、新しいメール 新しいメールアドレス / 新しいメールアドレスを入力、ボタン メールアドレスを変更 です。
子ブロックを使って、制御された state の動作を保ちながらレイアウトをカスタマイズします。
ライブエディター
function Example() { const defaultData = {newEmail: ''}; const [data, setData] = React.useState(defaultData); const [errors, setErrors] = React.useState({}); const handleDataChange = (nextData, meta) => { const {[meta.name]: _removed, ...restErrors} = errors; let nextErrors = restErrors; // Validate required fields on blur (same pattern as storybook) if (meta.cause === 'blur' && meta.name === 'newEmail') { if (!String(nextData.newEmail || '').trim()) { nextErrors = {...restErrors, newEmail: 'New email is required.'}; } } setErrors(nextErrors); setData(nextData); }; return ( <ChangeEmail data={data} currentEmail="user@example.com" labels={{currentEmailDisplay: 'Current email'}} errors={Object.keys(errors).length ? errors : undefined} onDataChange={handleDataChange} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} > <ChangeEmail.Title>Update your email</ChangeEmail.Title> <ChangeEmail.Form> <ChangeEmail.Form.CurrentEmailDisplay currentEmail="user@example.com" /> <ChangeEmail.Form.NewEmailField label="New email" placeholder="Enter your new email address" /> <ChangeEmail.Form.ChangeEmailButton>Update email</ChangeEmail.Form.ChangeEmailButton> </ChangeEmail.Form> </ChangeEmail> ); }
結果
Loading...
関数 children を使ってデフォルトのブロックと順序を上書きします。
ライブエディター
function Example() { const defaultData = {newEmail: ''}; const [data, setData] = React.useState(defaultData); const [errors, setErrors] = React.useState({}); const handleDataChange = (nextData, meta) => { const {[meta.name]: _removed, ...restErrors} = errors; let nextErrors = restErrors; // Validate required fields on blur (same pattern as storybook) if (meta.cause === 'blur' && meta.name === 'newEmail') { if (!String(nextData.newEmail || '').trim()) { nextErrors = {...restErrors, newEmail: 'New email is required.'}; } } setErrors(nextErrors); setData(nextData); }; return ( <ChangeEmail data={data} currentEmail="user@example.com" labels={{currentEmailDisplay: 'Current email'}} errors={Object.keys(errors).length ? errors : undefined} onDataChange={handleDataChange} onSubmit={e => { e.preventDefault(); alert(`Submit:\n${JSON.stringify(data, null, 2)}`); }} > {({defaultBlocks, defaultBlockOrder}) => ({ blocks: { ...defaultBlocks, customNotice: ( <div style={{ background: '#eef4ff', border: '1px solid #cddcff', borderRadius: '8px', padding: '12px', fontSize: '14px', marginBottom: 16, }} > 新しいアドレスには確認リンクを送信します。 </div> ), form: ( <ChangeEmail.Form> <ChangeEmail.Form.CurrentEmailDisplay currentEmail="user@example.com" /> <ChangeEmail.Form.NewEmailField label="New email" placeholder="Enter your new email address" /> <ChangeEmail.Form.ChangeEmailButton>Update email</ChangeEmail.Form.ChangeEmailButton> </ChangeEmail.Form> ), }, blockOrder: ['title', 'customNotice', ...defaultBlockOrder.filter(k => k !== 'title')], })} </ChangeEmail> ); }
結果
Loading...
ブロックオーバーライドを使うタイミング
順序を変えたいとき、デフォルトの UI ブロックを置き換えたいとき、共有 state の処理を維持しながらカスタムコンテンツを挿入したいときにオーバーライドを使います。デフォルトの順序は title, form です。
重要な props
コア props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
data | ChangeEmailFormData ({ newEmail? } or extended Record<string, unknown>) | Yes | - | 現在のフォーム data オブジェクト |
onDataChange | (data, meta) => void | Yes | - | 更新時に呼び出されます。meta には name, value, cause (input, change, blur, clear, reset, programmatic), optional event が含まれます |
errors | { [fieldName: string]: string | string[] } | No | undefined | data のフィールド名(例: newEmail)に紐づくフィールドエラー |
コンテンツ props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
currentEmail | string | No | undefined | 読み取り専用の現在のメールアドレス |
labels | { currentEmailDisplay?: string } | No | currentEmailDisplay: '現在のメールアドレス' | 現在のメール表示に対するラベルのみ。タイトル、フィールド、ボタンの文言はサブコンポーネントの props で上書きしてください。 |
レイアウトと構成 props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | BlocksOverride | ReactNode | No | undefined | デフォルトのブロックを上書きするか、カスタムの JSX children を提供します |
component | React.ElementType | No | 'form' | ルート要素の型 |
className | string | No | undefined | ルートコンテナのクラス名 |
sx | SxProps | No | undefined | ルートコンテナ向けの MUI システムスタイル |
ChangeEmail は StackProps<'form'>(children を除く)も継承するため、onSubmit、id、noValidate などの標準的なフォーム props がサポートされます。オーバーライドなしのデフォルトのブロック順は title, form (defaultBlockOrder) です。
デフォルト UI ブロック
| Block | Built on | Notes |
|---|---|---|
ChangeEmail (root) | Stack (component="form") | 制御されたフォームコンテナ。内部コンテンツの最大幅は 684px |
ChangeEmail.Title | Typography | タイトル(variant="h6")。デフォルト メールアドレスを変更 |
ChangeEmail.Form | Stack | フォームフィールドのコンテナ(送信はルート form が処理) |
ChangeEmail.Form.CurrentEmailDisplay | Typography + Stack | 読み取り専用の現在のメール。ラベルのデフォルトは 現在のメールアドレス(labels.currentEmailDisplay 経由)、値はルートの currentEmail を使用 |
ChangeEmail.Form.NewEmailField | TextField | 新しいメール(name="newEmail", type="text", autoComplete="new-email", fullWidth)。デフォルトは 新しいメールアドレス / 新しいメールアドレスを入力 |
ChangeEmail.Form.ChangeEmailButton | Button | 送信ボタン(variant="contained", type="submit")。デフォルト メールアドレスを変更 |
TypeScript
import {ChangeEmail, ChangeEmailFormData} from '@nodeblocks/frontend-change-email-block';
type MyChangeEmailData = ChangeEmailFormData & {
confirmToken?: string;
};