mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 03:52:26 +08:00
Merge pull request #6847 from ouchihao/codex/issue-4064-custom-page-button-visibility
feat(settings): allow hiding the custom page open button
This commit is contained in:
@@ -9,13 +9,14 @@ import (
|
||||
|
||||
// CustomMenuItem represents a user-configured custom menu entry.
|
||||
type CustomMenuItem struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
IconSVG string `json:"icon_svg"`
|
||||
URL string `json:"url"`
|
||||
PageSlug string `json:"page_slug,omitempty"`
|
||||
Visibility string `json:"visibility"` // "user" or "admin"
|
||||
SortOrder int `json:"sort_order"`
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
IconSVG string `json:"icon_svg"`
|
||||
URL string `json:"url"`
|
||||
PageSlug string `json:"page_slug,omitempty"`
|
||||
Visibility string `json:"visibility"` // "user" or "admin"
|
||||
SortOrder int `json:"sort_order"`
|
||||
HideOpenButton bool `json:"hide_open_button,omitempty"`
|
||||
}
|
||||
|
||||
// CustomEndpoint represents an admin-configured API endpoint for quick copy.
|
||||
|
||||
@@ -677,6 +677,7 @@ export default {
|
||||
namePlaceholder: 'e.g. Help Center',
|
||||
url: 'Page URL',
|
||||
urlPlaceholder: 'https://example.com/page',
|
||||
hideOpenButton: 'Hide the “Open in new tab” button',
|
||||
iconSvg: 'SVG Icon',
|
||||
iconSvgPlaceholder: '<svg>...</svg>',
|
||||
iconPreview: 'Icon Preview',
|
||||
|
||||
@@ -672,6 +672,7 @@ export default {
|
||||
namePlaceholder: '如:帮助中心',
|
||||
url: '页面 URL',
|
||||
urlPlaceholder: 'https://example.com/page',
|
||||
hideOpenButton: '隐藏“新窗口打开”按钮',
|
||||
iconSvg: 'SVG 图标',
|
||||
iconSvgPlaceholder: '<svg>...</svg>',
|
||||
iconPreview: '图标预览',
|
||||
|
||||
@@ -191,6 +191,7 @@ export interface CustomMenuItem {
|
||||
icon_svg: string
|
||||
url: string
|
||||
page_slug?: string
|
||||
hide_open_button?: boolean
|
||||
visibility: 'user' | 'admin'
|
||||
sort_order: number
|
||||
}
|
||||
|
||||
@@ -6763,6 +6763,17 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label class="flex items-center gap-2 sm:col-span-2">
|
||||
<input
|
||||
v-model="item.hide_open_button"
|
||||
type="checkbox"
|
||||
data-testid="custom-menu-hide-open-button"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">
|
||||
{{ t("admin.settings.customMenu.hideOpenButton") }}
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<!-- SVG Icon (full width) -->
|
||||
<div class="sm:col-span-2">
|
||||
<label
|
||||
@@ -9617,6 +9628,7 @@ const form = reactive<SettingsForm>({
|
||||
url: string;
|
||||
visibility: "user" | "admin";
|
||||
sort_order: number;
|
||||
hide_open_button?: boolean;
|
||||
}>,
|
||||
custom_endpoints: [] as Array<{
|
||||
name: string;
|
||||
|
||||
@@ -720,6 +720,31 @@ describe("admin SettingsView payment visible method controls", () => {
|
||||
adminSettingsFetch.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("loads and saves the open button visibility for each custom menu", async () => {
|
||||
const menuItems = [
|
||||
{ id: "docs", label: "Docs", url: "https://example.com/docs", icon_svg: "", visibility: "user", sort_order: 0 },
|
||||
{ id: "help", label: "Help", url: "https://example.com/help", icon_svg: "", visibility: "user", sort_order: 1, hide_open_button: true },
|
||||
];
|
||||
getSettings.mockResolvedValue({ ...baseSettingsResponse, custom_menu_items: menuItems });
|
||||
const wrapper = mountView();
|
||||
await flushPromises();
|
||||
|
||||
const toggles = wrapper.findAll<HTMLInputElement>('[data-testid="custom-menu-hide-open-button"]');
|
||||
expect(toggles.map(toggle => toggle.element.checked)).toEqual([false, true]);
|
||||
await toggles[0].setValue(true);
|
||||
await toggles[1].setValue(false);
|
||||
await wrapper.find("form").trigger("submit.prevent");
|
||||
await flushPromises();
|
||||
|
||||
expect(updateSettings).toHaveBeenCalledWith(expect.objectContaining({
|
||||
custom_menu_items: [
|
||||
{ ...menuItems[0], hide_open_button: true },
|
||||
{ ...menuItems[1], hide_open_button: false },
|
||||
],
|
||||
}));
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it("submits the compact home page toggle", async () => {
|
||||
const wrapper = mountView();
|
||||
await flushPromises();
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
<!-- Iframe embed mode -->
|
||||
<div v-else ref="embedShell" class="custom-embed-shell">
|
||||
<a
|
||||
v-if="!menuItem?.hide_open_button"
|
||||
ref="openButton"
|
||||
:href="embeddedUrl"
|
||||
target="_blank"
|
||||
|
||||
@@ -79,6 +79,13 @@ describe('custom page open button', () => {
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
it.each([undefined, false, true])('honors the per-menu hide button setting %s while keeping the iframe', (hidden) => {
|
||||
Object.assign(appStore.cachedPublicSettings.custom_menu_items[0], { hide_open_button: hidden })
|
||||
const wrapper = mountPage()
|
||||
expect(wrapper.find('.custom-open-fab').exists()).toBe(hidden !== true)
|
||||
expect(wrapper.get('iframe').attributes('src')).toContain('https://example.com/docs')
|
||||
})
|
||||
|
||||
it('preserves the embedded URL, secure link attributes, and normal clicks with small pointer movements', async () => {
|
||||
const { wrapper, button } = mountEmbed()
|
||||
expect(button.href).toBe(wrapper.get('iframe').attributes('src'))
|
||||
|
||||
Reference in New Issue
Block a user