import type { Meta, StoryObj } from '@storybook/html';
import { SmsEditor, type SmsEditorOptions } from '@42/core/sms-editor';
import { Picker } from 'emoji-mart';
import '@42/styles/sms-editor.css';
/** Default emoji set — swap this array (or plug a 3rd-party picker) to change it. */
const DEFAULT_EMOJIS = ['😀', '😂', '❤️', '🔥', '👍', '🎉', '🚀', '✨', '💯', '🙌', '👏', '😎'];
const HTML = `
0 chars
0 segment(s)
GSM-7
`;
/** Build a simple inline emoji grid from a customizable list. */
function gridPicker(emojis: string[]) {
return (container: HTMLElement, insert: (emoji: string) => void): void => {
container.classList.add('c42-sms-editor-panel--grid');
container.innerHTML = emojis
.map((e) => ``)
.join('');
container.addEventListener('click', (ev) => {
const btn = (ev.target as HTMLElement).closest('button');
if (btn) insert(btn.textContent!);
});
};
}
const meta: Meta = {
title: 'Core/Inputs & Forms/SmsEditor',
tags: ['autodocs'],
argTypes: {
maxSegments: { control: { type: 'number', min: 1, max: 10 } },
},
args: {},
};
export default meta;
type Story = StoryObj;
/**
* Live encoding + segment counting, with the emoji picker anchored bottom-right
* (closed until you tap 🙂). Add an emoji to flip GSM-7 → UCS-2.
*/
export const Default: Story = {
render: (args) => {
const wrapper = document.createElement('div');
wrapper.innerHTML = HTML;
const root = wrapper.querySelector('[data-c42-sms-editor]')!;
new SmsEditor(root, { ...args, renderPicker: gridPicker(DEFAULT_EMOJIS) });
return wrapper;
},
};
/**
* ## Custom emoji set
*
* The emoji selection is fully user-controlled — pass any list (or a 3rd-party
* picker) through `renderPicker`. Note: any emoji forces UCS-2 encoding.
*/
export const CustomEmojiSet: Story = {
render: (args) => {
const wrapper = document.createElement('div');
wrapper.innerHTML = HTML;
const root = wrapper.querySelector('[data-c42-sms-editor]')!;
const business = ['🥳', '🤝', '📦', '⏰', '✅', '❌', '💳', '📍', '📞', '⭐'];
new SmsEditor(root, { ...args, renderPicker: gridPicker(business) });
return wrapper;
},
};
/** With a 1-segment limit: typing past 160 GSM-7 chars flags `data-over-limit`. */
export const SingleSegmentLimit: Story = {
render: (args) => {
const wrapper = document.createElement('div');
wrapper.innerHTML = HTML;
const root = wrapper.querySelector('[data-c42-sms-editor]')!;
new SmsEditor(root, { ...args, maxSegments: 1, renderPicker: gridPicker(DEFAULT_EMOJIS) });
return wrapper;
},
args: { maxSegments: 1 },
};
/**
* ## Device preview (opt-in)
*
* Add a `[data-c42-sms-preview]` element (the controller fills it with the live,
* escaped message text) and wrap it in `.c42-sms-editor-device` to render it on
* an iPhone header background (the image ships with `@42/styles`). Entirely
* optional — omit the wrapper for a plain bubble, or omit the preview element
* entirely to keep the editor counters-only. Positioning is tunable via CSS
* custom properties (`--c42-sms-device-pad-top`, `--c42-sms-device-pad-x`).
*
* ```html
*
* ```
*/
export const DevicePreview: Story = {
render: (args) => {
const wrapper = document.createElement('div');
wrapper.innerHTML = `
0 chars
0 segment(s)
GSM-7
`;
const root = wrapper.querySelector('[data-c42-sms-editor]')!;
new SmsEditor(root, {
...args,
value: 'Your code is 4242. It expires in 10 minutes.',
emojis: false,
});
return wrapper;
},
};
/**
* ## With emoji-mart (external plugin)
*
* `emoji-mart` is not bundled with `@42/core`. Install it separately and mount
* it through `renderPicker`. Inserting any emoji forces UCS-2 encoding (watch
* the segment count drop from 160 → 70).
*
* ```bash
* pnpm add emoji-mart @emoji-mart/data
* ```
*/
export const WithEmojiMart: Story = {
render: (args) => {
const wrapper = document.createElement('div');
wrapper.innerHTML = `
Note: emoji-mart is not part of @42/core.
Install separately: pnpm add emoji-mart @emoji-mart/data
${HTML}`;
const root = wrapper.querySelector('[data-c42-sms-editor]')!;
new SmsEditor(root, {
...args,
renderPicker: (container, insert) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
new (Picker as any)({
parent: container,
onEmojiSelect: (emoji: { native: string }) => insert(emoji.native),
theme: 'light',
previewPosition: 'none',
searchPosition: 'none',
data: async () => {
const res = await fetch('https://cdn.jsdelivr.net/npm/@emoji-mart/data');
return res.json();
},
});
},
});
return wrapper;
},
};