feat(editor): add page emoji display toggle #14987 (#14999)

This PR adds a display toggle for Page Emoji, so users can choose
whether the add emoji option is shown in the page header when no emoji
is set.

What changed
read editor setting for display add icon option
hide emoji placeholder entry when the setting is disabled
keep existing behavior for readonly mode and for pages that already have
an emoji
Why
This implements the feature request to control Page Emoji visibility and
improves header cleanliness for users who prefer a minimal UI.

Issue
Closes #14093
<img width="1277" height="726" alt="Screenshot 2026-05-19 at 3 44 14 PM"
src="https://github.com/user-attachments/assets/caa29272-35c0-410d-bd54-2e038e4e0db2"
/>
<img width="1511" height="779" alt="Screenshot 2026-05-19 at 3 44 35 PM"
src="https://github.com/user-attachments/assets/3504136a-d34c-45cc-992b-0056b018ff92"
/>

Testing
verified in editable mode:
setting ON: add emoji placeholder is visible when page has no emoji
setting OFF: add emoji placeholder is hidden when page has no emoji
verified in readonly mode:
no emoji: nothing shown
with emoji: existing emoji is shown
verified no regression for selecting/changing/removing emoji
Screenshots
I will attach screenshots in this section.

Quick rule checks before submit

Base branch is canary.
PR title follows conventional format: type(scope): subject.
Scope editor is valid for this repo.
Include Closes #14093 in the body.
Add your screenshots before creating or right after opening the PR.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added an editor setting to toggle whether the "add icon" option is
shown when creating new documents (default: enabled).
* **User Experience**
* When disabled, the add-icon trigger is hidden for documents that use a
placeholder icon; readonly display remains unchanged.
* **Tests**
  * Updated tests to cover the new setting and toggle behavior.
* **Localization**
* Added translations and updated i18n typings and completeness metrics.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/toeverything/AFFiNE/pull/14999?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Waqar Bin zafar
2026-05-20 19:20:12 +05:00
committed by GitHub
parent 41145961f9
commit d20dbfd6a2
7 changed files with 63 additions and 4 deletions
@@ -1,4 +1,5 @@
import { IconEditor, IconRenderer } from '@affine/component';
import { EditorSettingService } from '@affine/core/modules/editor-setting';
import { ExplorerIconService } from '@affine/core/modules/explorer-icon/services/explorer-icon';
import { useI18n } from '@affine/i18n';
import { SmileSolidIcon } from '@blocksuite/icons/rc';
@@ -35,10 +36,13 @@ export const DocIconPicker = ({
}) => {
const t = useI18n();
const explorerIconService = useService(ExplorerIconService);
const editorSetting = useService(EditorSettingService).editorSetting;
const icon = useLiveData(explorerIconService.icon$('doc', docId));
const settings = useLiveData(editorSetting.settings$);
const isPlaceholder = !icon?.icon;
const shouldShowAddIconOption = settings.displayAddIconOption;
if (readonly) {
return isPlaceholder ? null : (
@@ -51,6 +55,10 @@ export const DocIconPicker = ({
);
}
if (isPlaceholder && !shouldShowAddIconOption) {
return null;
}
return (
<TitleContainer hasIcon={!isPlaceholder}>
<IconEditor
@@ -14,6 +14,7 @@ const editorSettingService = {
value: {
autoTitleNewDocWithCurrentDate: true,
newDocDateTitleFormat: 'DD-MM-YYYY',
displayAddIconOption: true,
},
},
set: editorSettingSet,
@@ -38,6 +39,10 @@ vi.mock('@affine/i18n', () => {
'YYYY-MM-DD',
'com.affine.settings.editorSettings.general.auto-date-title.format.journal':
'Journal style (localized)',
'com.affine.settings.editorSettings.general.add-icon-option.title':
'Display add icon option',
'com.affine.settings.editorSettings.general.add-icon-option.description':
'Show or hide the add icon option for docs without an icon.',
};
const useI18n = () =>
@@ -86,6 +91,7 @@ describe('NewDocDateTitleSettings', () => {
editorSettingService.editorSetting['settings$'].value = {
autoTitleNewDocWithCurrentDate: true,
newDocDateTitleFormat: 'DD-MM-YYYY',
displayAddIconOption: true,
};
});
@@ -97,7 +103,7 @@ describe('NewDocDateTitleSettings', () => {
test('persists the auto title toggle through EditorSettingService', () => {
render(<NewDocDateTitleSettings />);
fireEvent.click(screen.getByRole('checkbox'));
fireEvent.click(screen.getByTestId('auto-title-new-doc-trigger'));
expect(editorSettingSet).toHaveBeenCalledWith(
'autoTitleNewDocWithCurrentDate',
@@ -140,6 +146,7 @@ describe('NewDocDateTitleSettings', () => {
editorSettingService.editorSetting['settings$'].value = {
autoTitleNewDocWithCurrentDate: false,
newDocDateTitleFormat: 'DD-MM-YYYY',
displayAddIconOption: true,
};
render(<NewDocDateTitleSettings />);
@@ -149,4 +156,15 @@ describe('NewDocDateTitleSettings', () => {
).toBeNull();
expect(screen.queryByText('New doc date format')).toBeNull();
});
test('persists the add icon option toggle through EditorSettingService', () => {
render(<NewDocDateTitleSettings />);
fireEvent.click(screen.getByTestId('display-add-icon-option-trigger'));
expect(editorSettingSet).toHaveBeenCalledWith(
'displayAddIconOption',
false
);
});
});
@@ -464,6 +464,13 @@ export const NewDocDateTitleSettings = () => {
[editorSettingService.editorSetting]
);
const onToggleDisplayAddIconOption = useCallback(
(checked: boolean) => {
editorSettingService.editorSetting.set('displayAddIconOption', checked);
},
[editorSettingService.editorSetting]
);
return (
<>
<SettingRow
@@ -475,10 +482,25 @@ export const NewDocDateTitleSettings = () => {
]()}
>
<Switch
data-testid="auto-title-new-doc-trigger"
checked={settings.autoTitleNewDocWithCurrentDate}
onChange={onToggleAutoDateTitle}
/>
</SettingRow>
<SettingRow
name={t.t(
'com.affine.settings.editorSettings.general.add-icon-option.title'
)}
desc={t.t(
'com.affine.settings.editorSettings.general.add-icon-option.description'
)}
>
<Switch
data-testid="display-add-icon-option-trigger"
checked={settings.displayAddIconOption}
onChange={onToggleDisplayAddIconOption}
/>
</SettingRow>
{settings.autoTitleNewDocWithCurrentDate ? (
<SettingRow
name={t[
@@ -33,6 +33,7 @@ const AffineEditorSettingSchema = z.object({
newDocDateTitleFormat: z
.enum(newDocDateTitleFormatOptions)
.default('DD-MM-YYYY'),
displayAddIconOption: z.boolean().default(true),
fullWidthLayout: z.boolean().default(false),
displayDocInfo: z.boolean().default(true),
displayBiDirectionalLink: z.boolean().default(true),
@@ -3,12 +3,12 @@
"ca": 94,
"da": 4,
"de": 97,
"el-GR": 93,
"el-GR": 92,
"en": 100,
"es-AR": 93,
"es-CL": 94,
"es": 93,
"fa": 93,
"fa": 92,
"fr": 97,
"hi": 1,
"it": 94,
@@ -17,7 +17,7 @@
"ko": 93,
"nb-NO": 46,
"pl": 94,
"pt-BR": 93,
"pt-BR": 92,
"ru": 95,
"sv-SE": 93,
"uk": 93,
+8
View File
@@ -5538,6 +5538,14 @@ export function useAFFiNEI18N(): {
* `Journal style (localized)`
*/
["com.affine.settings.editorSettings.general.auto-date-title.format.journal"](): string;
/**
* `Display add icon option`
*/
["com.affine.settings.editorSettings.general.add-icon-option.title"](): string;
/**
* `Show or hide the add icon option for docs without an icon.`
*/
["com.affine.settings.editorSettings.general.add-icon-option.description"](): string;
/**
* `Customize your text experience.`
*/
@@ -1378,6 +1378,8 @@
"com.affine.settings.editorSettings.general.auto-date-title.format.mm-dd-yyyy": "MM-DD-YYYY",
"com.affine.settings.editorSettings.general.auto-date-title.format.yyyy-mm-dd": "YYYY-MM-DD",
"com.affine.settings.editorSettings.general.auto-date-title.format.journal": "Journal style (localized)",
"com.affine.settings.editorSettings.general.add-icon-option.title": "Display add icon option",
"com.affine.settings.editorSettings.general.add-icon-option.description": "Show or hide the add icon option for docs without an icon.",
"com.affine.settings.editorSettings.general.font-family.custom.description": "Customize your text experience.",
"com.affine.settings.editorSettings.general.font-family.custom.title": "Custom font family",
"com.affine.settings.editorSettings.general.font-family.description": "Choose your editor's font family.",