Skip to content

Commit

Permalink
Merge pull request #23381 from storybookjs/kasper/disable-whatsnew
Browse files Browse the repository at this point in the history
Settings: Disable whatsnew notification UI
  • Loading branch information
kasperpeulen authored Jul 10, 2023
2 parents 5f58f5c + 56378d1 commit d729de1
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 41 deletions.
2 changes: 1 addition & 1 deletion code/lib/core-common/src/utils/get-storybook-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const getRendererInfo = (packageJson: PackageJson) => {

const validConfigExtensions = ['ts', 'js', 'tsx', 'jsx', 'mjs', 'cjs'];

const findConfigFile = (prefix: string, configDir: string) => {
export const findConfigFile = (prefix: string, configDir: string) => {
const filePrefix = path.join(configDir, prefix);
const extension = validConfigExtensions.find((ext: string) =>
fse.existsSync(`${filePrefix}.${ext}`)
Expand Down
4 changes: 4 additions & 0 deletions code/lib/core-events/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ enum events {
REQUEST_WHATS_NEW_DATA = 'requestWhatsNewData',
RESULT_WHATS_NEW_DATA = 'resultWhatsNewData',
SET_WHATS_NEW_CACHE = 'setWhatsNewCache',
TOGGLE_WHATS_NEW_NOTIFICATIONS = 'toggleWhatsNewNotifications',
}

// Enables: `import Events from ...`
Expand Down Expand Up @@ -118,6 +119,7 @@ export const {
REQUEST_WHATS_NEW_DATA,
RESULT_WHATS_NEW_DATA,
SET_WHATS_NEW_CACHE,
TOGGLE_WHATS_NEW_NOTIFICATIONS,
} = events;

// Used to break out of the current render without showing a redbox
Expand All @@ -133,10 +135,12 @@ export type WhatsNewData =
status: 'SUCCESS';
title: string;
url: string;
blogUrl?: string;
publishedAt: string;
excerpt: string;
postIsRead: boolean;
showNotification: boolean;
disableWhatsNewNotifications: boolean;
}
| {
status: 'ERROR';
Expand Down
50 changes: 46 additions & 4 deletions code/lib/core-server/src/presets/common-preset.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { pathExists, readFile } from 'fs-extra';
import { deprecate, logger } from '@storybook/node-logger';
import { telemetry } from '@storybook/telemetry';
import {
findConfigFile,
getDirectoryFromWorkingDir,
getPreviewBodyTemplate,
getPreviewHeadTemplate,
Expand All @@ -15,7 +17,7 @@ import type {
StorybookConfig,
StoryIndexer,
} from '@storybook/types';
import { loadCsf } from '@storybook/csf-tools';
import { loadCsf, readConfig, writeConfig } from '@storybook/csf-tools';
import { join } from 'path';
import { dedent } from 'ts-dedent';
import fetch from 'node-fetch';
Expand All @@ -25,9 +27,11 @@ import {
REQUEST_WHATS_NEW_DATA,
RESULT_WHATS_NEW_DATA,
SET_WHATS_NEW_CACHE,
TOGGLE_WHATS_NEW_NOTIFICATIONS,
} from '@storybook/core-events';
import { parseStaticDir } from '../utils/server-statics';
import { defaultStaticDirs } from '../utils/constants';
import { sendTelemetryError } from '../withTelemetry';

const interpolate = (string: string, data: Record<string, string> = {}) =>
Object.entries(data).reduce((acc, [k, v]) => acc.replace(new RegExp(`%${k}%`, 'g'), v), string);
Expand Down Expand Up @@ -188,7 +192,6 @@ export const features = async (
storyStoreV7: true,
argTypeTargetsV7: true,
legacyDecoratorFileOrder: false,
whatsNewNotifications: false,
});

export const storyIndexers = async (indexers?: StoryIndexer[]) => {
Expand Down Expand Up @@ -246,10 +249,18 @@ const WHATS_NEW_CACHE = 'whats-new-cache';
const WHATS_NEW_URL = 'https://storybook.js.org/whats-new/v1';

// Grabbed from the implementation: https://github.com/storybookjs/dx-functions/blob/main/netlify/functions/whats-new.ts
type WhatsNewResponse = { title: string; url: string; publishedAt: string; excerpt: string };
type WhatsNewResponse = {
title: string;
url: string;
blogUrl?: string;
publishedAt: string;
excerpt: string;
};

// eslint-disable-next-line @typescript-eslint/naming-convention
export const experimental_serverChannel = (channel: Channel, options: Options) => {
export const experimental_serverChannel = async (channel: Channel, options: Options) => {
const coreOptions = await options.presets.apply('core');

channel.on(SET_WHATS_NEW_CACHE, async (data: WhatsNewCache) => {
const cache: WhatsNewCache = await options.cache.get(WHATS_NEW_CACHE).catch((e) => {
logger.verbose(e);
Expand All @@ -266,12 +277,19 @@ export const experimental_serverChannel = (channel: Channel, options: Options) =
throw response;
})) as WhatsNewResponse;

const main = await readConfig(findConfigFile('main', options.configDir));
const disableWhatsNewNotifications = main.getFieldValue([
'core',
'disableWhatsNewNotifications',
]);

const cache: WhatsNewCache = (await options.cache.get(WHATS_NEW_CACHE)) ?? {};
const data = {
...post,
status: 'SUCCESS',
postIsRead: post.url === cache.lastReadPost,
showNotification: post.url !== cache.lastDismissedPost && post.url !== cache.lastReadPost,
disableWhatsNewNotifications,
} satisfies WhatsNewData;
channel.emit(RESULT_WHATS_NEW_DATA, { data });
} catch (e) {
Expand All @@ -282,5 +300,29 @@ export const experimental_serverChannel = (channel: Channel, options: Options) =
}
});

channel.on(
TOGGLE_WHATS_NEW_NOTIFICATIONS,
async ({ disableWhatsNewNotifications }: { disableWhatsNewNotifications: boolean }) => {
const isTelemetryEnabled = coreOptions.disableTelemetry !== true;
try {
const main = await readConfig(findConfigFile('main', options.configDir));
main.setFieldValue(['core', 'disableWhatsNewNotifications'], disableWhatsNewNotifications);
await writeConfig(main);

if (isTelemetryEnabled) {
await telemetry('core-config', { disableWhatsNewNotifications });
}
} catch (error) {
if (isTelemetryEnabled) {
await sendTelemetryError(error, 'core-config', {
cliOptions: options,
presetOptions: { ...options, corePresets: [], overridePresets: [] },
skipPrompt: true,
});
}
}
}
);

return channel;
};
11 changes: 10 additions & 1 deletion code/lib/core-server/src/withTelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type TelemetryOptions = {
cliOptions: CLIOptions;
presetOptions?: Parameters<typeof loadAllPresets>[0];
printError?: (err: any) => void;
skipPrompt?: boolean;
};

const promptCrashReports = async () => {
Expand All @@ -30,7 +31,11 @@ const promptCrashReports = async () => {

type ErrorLevel = 'none' | 'error' | 'full';

async function getErrorLevel({ cliOptions, presetOptions }: TelemetryOptions): Promise<ErrorLevel> {
async function getErrorLevel({
cliOptions,
presetOptions,
skipPrompt,
}: TelemetryOptions): Promise<ErrorLevel> {
if (cliOptions.disableTelemetry) return 'none';

// If we are running init or similar, we just have to go with true here
Expand All @@ -54,6 +59,10 @@ async function getErrorLevel({ cliOptions, presetOptions }: TelemetryOptions): P
(await cache.get('enableCrashReports')) ?? (await cache.get('enableCrashreports'));
if (valueFromCache !== undefined) return valueFromCache ? 'full' : 'error';

if (skipPrompt) {
return 'error';
}

const valueFromPrompt = await promptCrashReports();
if (valueFromPrompt !== undefined) return valueFromPrompt ? 'full' : 'error';

Expand Down
15 changes: 14 additions & 1 deletion code/lib/manager-api/src/modules/whatsnew.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
REQUEST_WHATS_NEW_DATA,
RESULT_WHATS_NEW_DATA,
SET_WHATS_NEW_CACHE,
TOGGLE_WHATS_NEW_NOTIFICATIONS,
} from '@storybook/core-events';
import type { ModuleFn } from '../index';

Expand All @@ -14,6 +15,7 @@ export type SubState = {
export type SubAPI = {
isWhatsNewUnread(): boolean;
whatsNewHasBeenRead(): void;
toggleWhatsNewNotifications(): void;
};

const WHATS_NEW_NOTIFICATION_ID = 'whats-new';
Expand All @@ -39,6 +41,17 @@ export const init: ModuleFn = ({ fullAPI, store }) => {
fullAPI.clearNotification(WHATS_NEW_NOTIFICATION_ID);
}
},
toggleWhatsNewNotifications() {
if (state.whatsNewData?.status === 'SUCCESS') {
setWhatsNewState({
...state.whatsNewData,
disableWhatsNewNotifications: !state.whatsNewData.disableWhatsNewNotifications,
});
fullAPI.emit(TOGGLE_WHATS_NEW_NOTIFICATIONS, {
disableWhatsNewNotifications: state.whatsNewData.disableWhatsNewNotifications,
});
}
},
};

function getLatestWhatsNewPost(): Promise<WhatsNewData> {
Expand All @@ -63,9 +76,9 @@ export const init: ModuleFn = ({ fullAPI, store }) => {
const isNewStoryBookUser = fullAPI.getUrlState().path.includes('onboarding');

if (
global.FEATURES.whatsNewNotifications &&
!isNewStoryBookUser &&
whatsNewData.status === 'SUCCESS' &&
!whatsNewData.disableWhatsNewNotifications &&
whatsNewData.showNotification
) {
fullAPI.addNotification({
Expand Down
3 changes: 2 additions & 1 deletion code/lib/telemetry/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export type EventType =
| 'canceled'
| 'error'
| 'error-metadata'
| 'version-update';
| 'version-update'
| 'core-config';

export interface Dependency {
version: string | undefined;
Expand Down
10 changes: 5 additions & 5 deletions code/lib/types/src/modules/core-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export interface CoreConfig {
* @see https://storybook.js.org/telemetry
*/
disableTelemetry?: boolean;

/**
* Disables notifications for Storybook updates.
*/
disableWhatsNewNotifications?: boolean;
/**
* Enable crash reports to be sent to Storybook telemetry
* @see https://storybook.js.org/telemetry
Expand Down Expand Up @@ -305,11 +310,6 @@ export interface StorybookConfig {
* Apply decorators from preview.js before decorators from addons or frameworks
*/
legacyDecoratorFileOrder?: boolean;

/**
* Show a notification anytime a What's new? post is published in the Storybook blog.
*/
whatsNewNotifications?: boolean;
};

/**
Expand Down
7 changes: 3 additions & 4 deletions code/ui/manager/src/components/sidebar/Menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { ComponentProps } from 'react';
import React from 'react';
import { expect } from '@storybook/jest';
import type { Meta, StoryObj } from '@storybook/react';
import type { ComponentProps } from 'react';

import { TooltipLinkList } from '@storybook/components';
import { styled } from '@storybook/theming';
import { within, userEvent, screen } from '@storybook/testing-library';
import { screen, userEvent, within } from '@storybook/testing-library';
import type { State } from '@storybook/manager-api';
import { SidebarMenu, ToolbarMenu } from './Menu';
import { useMenu } from '../../containers/menu';
Expand Down Expand Up @@ -46,9 +46,8 @@ const DoubleThemeRenderingHack = styled.div({

export const Expanded: Story = {
render: () => {
window.FEATURES.whatsNewNotifications = true;
const menu = useMenu(
{ whatsNewData: { status: 'SUCCESS' } } as State,
{ whatsNewData: { status: 'SUCCESS', disableWhatsNewNotifications: false } } as State,
{
// @ts-expect-error (Converted from ts-ignore)
getShortcutKeys: () => ({}),
Expand Down
4 changes: 2 additions & 2 deletions code/ui/manager/src/containers/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Badge, Icons } from '@storybook/components';
import type { API, State } from '@storybook/manager-api';
import { shortcutToHumanString } from '@storybook/manager-api';
import { styled, useTheme } from '@storybook/theming';
import { global } from '@storybook/global';

const focusableUIElements = {
storySearchField: 'storybook-explorer-searchfield',
Expand Down Expand Up @@ -69,7 +68,8 @@ export const useMenu = (
[api]
);

const whatsNewNotificationsEnabled = global.FEATURES.whatsNewNotifications;
const whatsNewNotificationsEnabled =
state.whatsNewData?.status === 'SUCCESS' && !state.disableWhatsNewNotifications;
const isWhatsNewUnread = api.isWhatsNewUnread();
const whatsNew = useMemo(
() => ({
Expand Down
6 changes: 4 additions & 2 deletions code/ui/manager/src/containers/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React from 'react';
import type { Combo, StoriesHash } from '@storybook/manager-api';
import { Consumer } from '@storybook/manager-api';

import { global } from '@storybook/global';
import { Sidebar as SidebarComponent } from '../components/sidebar/Sidebar';
import { useMenu } from './menu';

Expand Down Expand Up @@ -33,6 +32,9 @@ const Sidebar = React.memo(function Sideber() {
enableShortcuts
);

const whatsNewNotificationsEnabled =
state.whatsNewData?.status === 'SUCCESS' && !state.disableWhatsNewNotifications;

return {
title: name,
url,
Expand All @@ -44,7 +46,7 @@ const Sidebar = React.memo(function Sideber() {
refId,
viewMode,
menu,
menuHighlighted: global.FEATURES.whatsNewNotifications && api.isWhatsNewUnread(),
menuHighlighted: whatsNewNotificationsEnabled && api.isWhatsNewUnread(),
enableShortcuts,
};
};
Expand Down
1 change: 1 addition & 0 deletions code/ui/manager/src/globals/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export default {
'STORY_SPECIFIED',
'STORY_THREW_EXCEPTION',
'STORY_UNCHANGED',
'TOGGLE_WHATS_NEW_NOTIFICATIONS',
'UPDATE_GLOBALS',
'UPDATE_QUERY_PARAMS',
'UPDATE_STORY_ARGS',
Expand Down
1 change: 1 addition & 0 deletions code/ui/manager/src/settings/about.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const UpgradeBlock = styled.div(({ theme }) => {
borderRadius: 5,
padding: 20,
margin: 20,
marginTop: 0,
maxWidth: 400,
borderColor: theme.appBorderColor,
fontSize: theme.typography.size.s2,
Expand Down
Loading

0 comments on commit d729de1

Please sign in to comment.