diff --git a/.eslintignore b/.eslintignore index 25dff64df32f..22a60fa7549f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -14,6 +14,7 @@ libtextsecure/components.js libtextsecure/test/test.js test/test.js ts/protobuf/compiled.d.ts +storybook-static/** # Third-party files js/Mp3LameEncoder.min.js diff --git a/.eslintrc.js b/.eslintrc.js index 46a94e77404b..47c8f72b554e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -300,6 +300,12 @@ module.exports = { 'local-rules/type-alias-readonlydeep': 'error', }, }, + { + files: ['ts/**/*_test.{ts,tsx}'], + rules: { + 'func-names': 'off', + }, + }, ], rules: { diff --git a/.github/workflows/stories.yml b/.github/workflows/stories.yml new file mode 100644 index 000000000000..d5ae8d77aeae --- /dev/null +++ b/.github/workflows/stories.yml @@ -0,0 +1,23 @@ +# Copyright 2023 Signal Messenger, LLC +# SPDX-License-Identifier: AGPL-3.0-only +name: Stories +on: + push: + branches: + - development + - main + - '[0-9]+.[0-9]+.x' + pull_request: +jobs: + test: + runs-on: ubuntu-latest-8-cores + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18.15.0' + cache: 'yarn' + - run: yarn install --frozen-lockfile + - run: yarn build:storybook + - run: yarn run-p --race test:storybook:serve test:storybook:test diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index 3310856f670a..000000000000 --- a/.storybook/main.js +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2022 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -module.exports = { - typescript: { - reactDocgen: false, - }, - stories: ['../ts/components/**/*.stories.tsx'], - addons: [ - '@storybook/addon-a11y', - '@storybook/addon-actions', - '@storybook/addon-controls', - '@storybook/addon-measure', - '@storybook/addon-toolbars', - '@storybook/addon-viewport', - - // This must be imported last. - '@storybook/addon-interactions', - - // Deprecated! Please remove when all uses have been migrated to controls. - '@storybook/addon-knobs', - ], - core: { - builder: 'webpack5', - }, - features: { - storyStoreV7: true, - }, -}; diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 000000000000..ebda446b1348 --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,98 @@ +// Copyright 2022 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { StorybookConfig } from '@storybook/react-webpack5'; +import { ProvidePlugin } from 'webpack'; + +const config: StorybookConfig = { + typescript: { + reactDocgen: false, + }, + stories: ['../ts/components/**/*.stories.tsx'], + addons: [ + '@storybook/addon-a11y', + '@storybook/addon-actions', + '@storybook/addon-controls', + '@storybook/addon-measure', + '@storybook/addon-toolbars', + '@storybook/addon-viewport', + '@storybook/addon-jest', + + // This must be imported last. + '@storybook/addon-interactions', + ], + framework: '@storybook/react-webpack5', + core: {}, + features: { + storyStoreV7: true, + }, + staticDirs: [ + { from: '../fonts', to: 'fonts' }, + { from: '../images', to: 'images' }, + { from: '../fixtures', to: 'fixtures' }, + ], + webpackFinal(config) { + config.cache = { + type: 'filesystem', + }; + + config.resolve!.extensions = ['.tsx', '.ts', '...']; + + config.module!.rules!.unshift({ + test: /\.scss$/, + use: [ + { loader: 'style-loader' }, + { loader: 'css-loader', options: { modules: false, url: false } }, + { loader: 'sass-loader' }, + ], + }); + + config.module!.rules!.unshift({ + test: /\.css$/, + use: [ + // prevent storybook defaults from being applied + ], + }); + + config.node = { global: true }; + + config.externals = { + net: 'commonjs net', + vm: 'commonjs vm', + fs: 'commonjs fs', + async_hooks: 'commonjs async_hooks', + module: 'commonjs module', + stream: 'commonjs stream', + tls: 'commonjs tls', + dns: 'commonjs dns', + http: 'commonjs http', + https: 'commonjs https', + os: 'commonjs os', + constants: 'commonjs constants', + zlib: 'commonjs zlib', + '@signalapp/libsignal-client': 'commonjs @signalapp/libsignal-client', + '@signalapp/libsignal-client/zkgroup': + 'commonjs @signalapp/libsignal-client/zkgroup', + '@signalapp/ringrtc': 'commonjs @signalapp/ringrtc', + '@signalapp/better-sqlite3': 'commonjs @signalapp/better-sqlite3', + electron: 'commonjs electron', + 'fs-xattr': 'commonjs fs-xattr', + fsevents: 'commonjs fsevents', + 'mac-screen-capture-permissions': + 'commonjs mac-screen-capture-permissions', + sass: 'commonjs sass', + bufferutil: 'commonjs bufferutil', + 'utf-8-validate': 'commonjs utf-8-validate', + }; + + config.plugins!.push( + new ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + }) + ); + + return config; + }, +}; + +export default config; diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html deleted file mode 100644 index d9cd9e8843b4..000000000000 --- a/.storybook/preview-head.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index ae3ec18f16b2..06afdc5784ea 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,17 +1,29 @@ // Copyright 2019 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only +import '../ts/window.d.ts'; + import React from 'react'; -import classnames from 'classnames'; -import { withKnobs, boolean, optionsKnob } from '@storybook/addon-knobs'; + +import 'sanitize.css'; +import '../stylesheets/manifest.scss'; +import '../node_modules/@indutny/frameless-titlebar/dist/styles.css'; import * as styles from './styles.scss'; import messages from '../_locales/en/messages.json'; -import { ClassyProvider } from '../ts/components/PopperRootContext'; import { StorybookThemeContext } from './StorybookThemeContext'; import { ThemeType } from '../ts/types/Util'; import { setupI18n } from '../ts/util/setupI18n'; import { HourCyclePreference } from '../ts/types/I18N'; +import { Provider } from 'react-redux'; +import { Store, combineReducers, createStore } from 'redux'; +import { StateType } from '../ts/state/reducer'; +import { + ScrollerLockContext, + createScrollerLock, +} from '../ts/hooks/useScrollLock'; + +const i18n = setupI18n('en', messages); export const globalTypes = { mode: { @@ -38,8 +50,73 @@ export const globalTypes = { }, }; -window.i18n = setupI18n('en', messages); -window.getHourCyclePreference = () => HourCyclePreference.UnknownPreference; +const mockStore: Store = createStore( + combineReducers({ + calling: (state = {}) => state, + conversations: ( + state = { + conversationLookup: {}, + targetedConversationPanels: {}, + } + ) => state, + globalModals: (state = {}) => state, + user: (state = {}) => state, + }) +); + +// eslint-disable-next-line +const noop = () => {}; + +window.Whisper = window.Whisper || {}; +window.Whisper.events = { + on: noop, +}; + +window.SignalContext = { + i18n, + + activeWindowService: { + isActive: () => true, + registerForActive: noop, + unregisterForActive: noop, + registerForChange: noop, + unregisterForChange: noop, + }, + + nativeThemeListener: { + getSystemTheme: () => 'light', + subscribe: noop, + unsubscribe: noop, + update: () => 'light', + }, + Settings: { + themeSetting: { + getValue: async () => 'light', + setValue: async () => 'light', + }, + waitForChange: () => new Promise(noop), + }, + OS: { + hasCustomTitleBar: () => false, + getClassName: () => '', + platform: '', + release: '', + }, + usernames: { + hash: input => Buffer.from(input), + } as any, + config: {} as any, + + getHourCyclePreference: () => HourCyclePreference.UnknownPreference, + getPreferredSystemLocales: () => ['en'], + getResolvedMessagesLocaleDirection: () => 'ltr', +}; + +window.i18n = i18n; +window.ConversationController = window.ConversationController || {}; +window.ConversationController.isSignalConversationId = () => false; +window.ConversationController.onConvoMessageMount = noop; +window.reduxStore = mockStore; const withModeAndThemeProvider = (Story, context) => { const theme = @@ -75,7 +152,29 @@ const withModeAndThemeProvider = (Story, context) => { ); }; -export const decorators = [withModeAndThemeProvider]; +function withMockStoreProvider(Story, context) { + return ( + + + + ); +} + +function withScrollLockProvider(Story, context) { + return ( + {})} + > + + + ); +} + +export const decorators = [ + withModeAndThemeProvider, + withMockStoreProvider, + withScrollLockProvider, +]; export const parameters = { axe: { diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js deleted file mode 100644 index c11fdbe046c4..000000000000 --- a/.storybook/webpack.config.js +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -const webpack = require('webpack'); - -module.exports = ({ config }) => { - config.entry.unshift('!!style-loader!css-loader!sanitize.css'); - - config.cache = { - type: 'filesystem', - }; - - config.module.rules.unshift({ - test: /\.scss$/, - use: [ - { loader: 'style-loader' }, - { loader: 'css-loader?modules=true&localsConvention=camelCaseOnly' }, - { loader: 'sass-loader' }, - ], - }); - - config.node = { global: true }; - - config.externals = { - net: 'commonjs net', - vm: 'commonjs vm', - fs: 'commonjs fs', - async_hooks: 'commonjs async_hooks', - module: 'commonjs module', - stream: 'commonjs stream', - tls: 'commonjs tls', - dns: 'commonjs dns', - http: 'commonjs http', - https: 'commonjs https', - os: 'commonjs os', - constants: 'commonjs constants', - zlib: 'commonjs zlib', - '@signalapp/libsignal-client': 'commonjs @signalapp/libsignal-client', - '@signalapp/libsignal-client/zkgroup': - 'commonjs @signalapp/libsignal-client/zkgroup', - '@signalapp/ringrtc': 'commonjs @signalapp/ringrtc', - '@signalapp/better-sqlite3': 'commonjs @signalapp/better-sqlite3', - electron: 'commonjs electron', - 'fs-xattr': 'commonjs fs-xattr', - fsevents: 'commonjs fsevents', - 'mac-screen-capture-permissions': 'commonjs mac-screen-capture-permissions', - sass: 'commonjs sass', - bufferutil: 'commonjs bufferutil', - 'utf-8-validate': 'commonjs utf-8-validate', - }; - - return config; -}; diff --git a/.yarnclean b/.yarnclean index 4593ef707b33..230d0afc6570 100644 --- a/.yarnclean +++ b/.yarnclean @@ -41,3 +41,6 @@ Gruntfile.js # asset directories !nyc/node_modules/istanbul-reports/lib/html/assets + +# bad matches +!patch-package/node_modules/yaml/dist/doc diff --git a/package.json b/package.json index cc393d360b8c..f54e3f8553b5 100644 --- a/package.json +++ b/package.json @@ -62,11 +62,11 @@ "clean-transpile": "yarn run clean-transpile-once && yarn run clean-transpile-once", "open-coverage": "open coverage/lcov-report/index.html", "ready": "npm-run-all --print-label clean-transpile generate --parallel lint lint-deps lint-intl test-node test-electron", - "dev": "run-p --print-label dev:*", - "dev:transpile": "run-p \"check:types --watch\" dev:esbuild", - "dev:esbuild": "node scripts/esbuild.js --watch", - "dev:storybook": "cross-env SIGNAL_ENV=storybook start-storybook -p 6006 -s ./", - "dev:sass": "yarn sass --watch", + "dev": "yarn build-protobuf && cross-env SIGNAL_ENV=storybook storybook dev --port 6006", + "build:storybook": "yarn build-protobuf && cross-env SIGNAL_ENV=storybook storybook build", + "test:storybook": "yarn build:storybook && run-p --race test:storybook:*", + "test:storybook:serve": "http-server storybook-static --port 6006 --silent", + "test:storybook:test": "wait-on http://127.0.0.1:6006/ --timeout 5000 && test-storybook", "build": "run-s --print-label generate build:esbuild:prod build:release", "build-linux": "yarn generate && yarn build:esbuild:prod && yarn build:release -- --publish=never", "build:acknowledgments": "node scripts/generate-acknowledgments.js", @@ -184,33 +184,33 @@ "zod": "3.21.4" }, "devDependencies": { - "@babel/core": "7.14.3", - "@babel/plugin-proposal-class-properties": "7.17.12", - "@babel/plugin-proposal-nullish-coalescing-operator": "7.17.12", - "@babel/plugin-proposal-optional-chaining": "7.17.12", - "@babel/plugin-transform-runtime": "7.18.2", - "@babel/plugin-transform-typescript": "7.18.4", - "@babel/preset-react": "7.17.12", - "@babel/preset-typescript": "7.17.12", + "@babel/core": "7.23.0", + "@babel/plugin-proposal-class-properties": "7.18.6", + "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6", + "@babel/plugin-proposal-optional-chaining": "7.21.0", + "@babel/plugin-transform-runtime": "7.22.15", + "@babel/plugin-transform-typescript": "7.22.15", + "@babel/preset-react": "7.22.15", + "@babel/preset-typescript": "7.23.0", "@electron/fuses": "1.5.0", "@formatjs/intl": "2.6.7", "@mixer/parallel-prettier": "2.0.3", "@signalapp/mock-server": "4.1.2", - "@storybook/addon-a11y": "6.5.6", - "@storybook/addon-actions": "6.5.6", - "@storybook/addon-controls": "6.5.6", - "@storybook/addon-interactions": "6.5.9", - "@storybook/addon-knobs": "6.4.0", - "@storybook/addon-measure": "6.5.6", - "@storybook/addon-toolbars": "6.5.6", - "@storybook/addon-viewport": "6.5.6", - "@storybook/addons": "6.5.6", - "@storybook/builder-webpack5": "6.5.15", - "@storybook/jest": "0.0.10", - "@storybook/manager-webpack5": "6.5.15", - "@storybook/react": "6.5.6", - "@storybook/testing-library": "0.0.13", - "@types/backbone": "1.4.5", + "@storybook/addon-a11y": "7.4.5", + "@storybook/addon-actions": "7.4.5", + "@storybook/addon-controls": "7.4.5", + "@storybook/addon-interactions": "7.4.5", + "@storybook/addon-jest": "7.4.5", + "@storybook/addon-measure": "7.4.5", + "@storybook/addon-toolbars": "7.4.5", + "@storybook/addon-viewport": "7.4.5", + "@storybook/addons": "7.4.5", + "@storybook/jest": "0.2.2", + "@storybook/react": "7.4.5", + "@storybook/react-webpack5": "7.4.5", + "@storybook/test-runner": "0.13.0", + "@storybook/testing-library": "0.2.2", + "@types/backbone": "1.4.16", "@types/blueimp-load-image": "5.14.1", "@types/chai": "4.2.18", "@types/chai-as-promised": "7.1.4", @@ -258,7 +258,7 @@ "asar": "3.1.0", "axe-core": "4.1.4", "babel-core": "7.0.0-bridge.0", - "babel-loader": "8.0.6", + "babel-loader": "9.1.3", "babel-plugin-lodash": "3.3.4", "casual": "1.6.2", "chai": "4.3.4", @@ -285,19 +285,23 @@ "eslint-plugin-react": "7.31.10", "execa": "5.1.1", "html-webpack-plugin": "5.3.1", + "http-server": "14.1.1", "json-to-ast": "2.1.0", + "mini-css-extract-plugin": "2.7.6", "mocha": "9.1.3", "node-gyp": "9.0.0", "npm-run-all": "4.1.5", "nyc": "11.4.1", "p-limit": "3.1.0", - "patch-package": "6.4.7", + "patch-package": "8.0.0", "playwright": "1.33.0", "prettier": "2.8.0", "protobufjs-cli": "1.1.1", + "resolve-url-loader": "5.0.0", "sass": "1.49.7", "sass-loader": "10.2.0", "sinon": "11.1.1", + "storybook": "7.4.5", "style-loader": "1.0.0", "stylelint": "15.4.0", "stylelint-config-css-modules": "4.2.0", @@ -309,7 +313,8 @@ "ts-node": "8.3.0", "typed-scss-modules": "4.1.1", "typescript": "5.1.3", - "webpack": "5.76.0", + "wait-on": "7.0.1", + "webpack": "5.88.2", "webpack-cli": "4.9.2", "webpack-dev-server": "4.11.1" }, diff --git a/patches/@storybook+manager-api+7.4.5.patch b/patches/@storybook+manager-api+7.4.5.patch new file mode 100644 index 000000000000..ecba613a45b4 --- /dev/null +++ b/patches/@storybook+manager-api+7.4.5.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@storybook/manager-api/dist/index.d.ts b/node_modules/@storybook/manager-api/dist/index.d.ts +index 7afb86e..a15b801 100644 +--- a/node_modules/@storybook/manager-api/dist/index.d.ts ++++ b/node_modules/@storybook/manager-api/dist/index.d.ts +@@ -822,7 +822,7 @@ declare class ManagerProvider extends Component { + static getDerivedStateFromProps(props: ManagerProviderProps, state: State): State; + shouldComponentUpdate(nextProps: ManagerProviderProps, nextState: State): boolean; + initModules: () => void; +- render(): React.JSX.Element; ++ render(): JSX.Element; + } + interface ManagerConsumerProps

{ + filter?: (combo: Combo) => P; diff --git a/patches/@storybook+router+7.4.5.patch b/patches/@storybook+router+7.4.5.patch new file mode 100644 index 000000000000..793a975dbee6 --- /dev/null +++ b/patches/@storybook+router+7.4.5.patch @@ -0,0 +1,22 @@ +diff --git a/node_modules/@storybook/router/dist/index.d.ts b/node_modules/@storybook/router/dist/index.d.ts +index ed699f7..71f48eb 100644 +--- a/node_modules/@storybook/router/dist/index.d.ts ++++ b/node_modules/@storybook/router/dist/index.d.ts +@@ -335,7 +335,7 @@ declare const useNavigate: () => (to: To | number, { plain, ...options }?: any) + * A component that will navigate to a new location/path when clicked + */ + declare const Link: { +- ({ to, children, ...rest }: LinkProps): React__default.JSX.Element; ++ ({ to, children, ...rest }: LinkProps): JSX.Element; + displayName: string; + }; + /** +@@ -343,7 +343,7 @@ declare const Link: { + * and will be called whenever it changes when it changes + */ + declare const Location: { +- ({ children }: LocationProps): React__default.JSX.Element; ++ ({ children }: LocationProps): JSX.Element; + displayName: string; + }; + /** diff --git a/patches/@types+backbone+1.4.16.patch b/patches/@types+backbone+1.4.16.patch new file mode 100644 index 000000000000..ed2430deb183 --- /dev/null +++ b/patches/@types+backbone+1.4.16.patch @@ -0,0 +1,49 @@ +diff --git a/node_modules/@types/backbone/index.d.ts b/node_modules/@types/backbone/index.d.ts +index 3935075..22a4c2e 100644 +--- a/node_modules/@types/backbone/index.d.ts ++++ b/node_modules/@types/backbone/index.d.ts +@@ -81,7 +81,7 @@ declare namespace Backbone { + collection?: Collection | undefined; + } + +- type CombinedModelConstructorOptions = Model> = ModelConstructorOptions & E; ++ type CombinedModelConstructorOptions = Model> = ModelConstructorOptions & E; + + interface ModelSetOptions extends Silenceable, Validable {} + +@@ -219,7 +219,7 @@ declare namespace Backbone { + */ + static extend(properties: any, classProperties?: any): any; + +- attributes: Partial; ++ attributes: T; + changed: Partial; + cidPrefix: string; + cid: string; +@@ -235,7 +235,7 @@ declare namespace Backbone { + * That works only if you set it in the constructor or the initialize method. + */ + defaults(): Partial; +- id: string | number; ++ id: string; + idAttribute: string; + validationError: any; + +@@ -266,7 +266,7 @@ declare namespace Backbone { + * return super.get("name"); + * } + */ +- get>(attributeName: A): T[A] | undefined; ++ get>(attributeName: A): T[A]; + + /** + * For strongly-typed assignment of attributes, use the `set` method only privately in public setter properties. +@@ -300,7 +300,7 @@ declare namespace Backbone { + previousAttributes(): Partial; + save(attributes?: Partial | null, options?: ModelSaveOptions): JQueryXHR; + unset(attribute: _StringKey, options?: Silenceable): this; +- validate(attributes: Partial, options?: any): any; ++ validate(attributes: T, options?: any): any; + private _validate(attributes: Partial, options: any): boolean; + + // mixins from underscore diff --git a/patches/@types+backbone+1.4.5.patch b/patches/@types+backbone+1.4.5.patch deleted file mode 100644 index 57bfcd8a8a68..000000000000 --- a/patches/@types+backbone+1.4.5.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/node_modules/@types/backbone/index.d.ts b/node_modules/@types/backbone/index.d.ts -index a172230..2c62e92 100644 ---- a/node_modules/@types/backbone/index.d.ts -+++ b/node_modules/@types/backbone/index.d.ts -@@ -81,7 +81,7 @@ declare namespace Backbone { - collection?: Backbone.Collection; - } - -- type CombinedModelConstructorOptions = Model> = ModelConstructorOptions & E; -+ type CombinedModelConstructorOptions = Model> = ModelConstructorOptions & E; - - interface ModelSetOptions extends Silenceable, Validable { - } -@@ -218,14 +218,14 @@ declare namespace Backbone { - * E - Extensions to the model constructor options. You can accept additional constructor options - * by listing them in the E parameter. - */ -- class Model extends ModelBase implements Events { -+ class Model = any, S = Backbone.ModelSetOptions, E = {}> extends ModelBase implements Events { - - /** - * Do not use, prefer TypeScript's extend functionality. - **/ - public static extend(properties: any, classProperties?: any): any; - -- attributes: any; -+ attributes: T; - changed: any[]; - cidPrefix: string; - cid: string; diff --git a/patches/@types+express+4.17.13.patch b/patches/@types+express+4.17.18.patch similarity index 100% rename from patches/@types+express+4.17.13.patch rename to patches/@types+express+4.17.18.patch diff --git a/patches/@types+jest+28.1.1.patch b/patches/@types+jest+28.1.3.patch similarity index 94% rename from patches/@types+jest+28.1.1.patch rename to patches/@types+jest+28.1.3.patch index b33bddd34a48..77ea181e8e60 100644 --- a/patches/@types+jest+28.1.1.patch +++ b/patches/@types+jest+28.1.3.patch @@ -1,10 +1,10 @@ diff --git a/node_modules/@types/jest/index.d.ts b/node_modules/@types/jest/index.d.ts -index 272822a..87ebd1f 100755 +index b37b188..1908c0c 100755 --- a/node_modules/@types/jest/index.d.ts +++ b/node_modules/@types/jest/index.d.ts @@ -30,18 +30,18 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - // Minimum TypeScript Version: 3.8 + // Minimum TypeScript Version: 4.3 -declare var beforeAll: jest.Lifecycle; -declare var beforeEach: jest.Lifecycle; diff --git a/ts/Crypto.ts b/ts/Crypto.ts index 09cee51db901..ceb3273b7046 100644 --- a/ts/Crypto.ts +++ b/ts/Crypto.ts @@ -13,8 +13,6 @@ import { getBytesSubarray } from './util/uuidToBytes'; export { HashType, CipherType }; -export const UUID_BYTE_SIZE = 16; - const PROFILE_IV_LENGTH = 12; // bytes const PROFILE_KEY_LENGTH = 32; // bytes diff --git a/ts/background.ts b/ts/background.ts index add167359199..ff7316ef7927 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -3288,13 +3288,14 @@ export async function startApp(): Promise { 'onDeliveryReceipt: missing valid sourceServiceId' ); strictAssert(sourceDevice, 'onDeliveryReceipt: missing sourceDevice'); + strictAssert(sourceConversation, 'onDeliveryReceipt: missing conversation'); const attributes: MessageReceiptAttributesType = { envelopeId: ev.deliveryReceipt.envelopeId, removeFromMessageReceiverCache: ev.confirm, messageSentAt: timestamp, receiptTimestamp: envelopeTimestamp, - sourceConversationId: sourceConversation?.id, + sourceConversationId: sourceConversation.id, sourceServiceId, sourceDevice, type: MessageReceipts.MessageReceiptType.Delivery, diff --git a/ts/components/AddGroupMemberErrorDialog.stories.tsx b/ts/components/AddGroupMemberErrorDialog.stories.tsx index 9ced0f47f75b..5db6945b6c0e 100644 --- a/ts/components/AddGroupMemberErrorDialog.stories.tsx +++ b/ts/components/AddGroupMemberErrorDialog.stories.tsx @@ -5,8 +5,10 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; +import type { PropsType } from './AddGroupMemberErrorDialog'; import { AddGroupMemberErrorDialog, AddGroupMemberErrorDialogMode, @@ -16,24 +18,22 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/AddGroupMemberErrorDialog', -}; +} satisfies Meta; const defaultProps = { i18n, onClose: action('onClose'), }; -export const _MaximumGroupSize = (): JSX.Element => ( - -); - -_MaximumGroupSize.story = { - name: 'Maximum group size', -}; +export function MaximumGroupSize(): JSX.Element { + return ( + + ); +} export function MaximumRecommendedGroupSize(): JSX.Element { return ( @@ -44,7 +44,3 @@ export function MaximumRecommendedGroupSize(): JSX.Element { /> ); } - -MaximumRecommendedGroupSize.story = { - name: 'Maximum recommended group size', -}; diff --git a/ts/components/AddGroupMemberErrorDialog.tsx b/ts/components/AddGroupMemberErrorDialog.tsx index cae318cfa251..7cb81962dc82 100644 --- a/ts/components/AddGroupMemberErrorDialog.tsx +++ b/ts/components/AddGroupMemberErrorDialog.tsx @@ -23,7 +23,7 @@ type PropsDataType = recommendedMaximumNumberOfContacts: number; }; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => void; } & PropsDataType; diff --git a/ts/components/AddUserToAnotherGroupModal.stories.tsx b/ts/components/AddUserToAnotherGroupModal.stories.tsx index 4848542fa9c8..b3ffe7e60745 100644 --- a/ts/components/AddUserToAnotherGroupModal.stories.tsx +++ b/ts/components/AddUserToAnotherGroupModal.stories.tsx @@ -1,8 +1,9 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React from 'react'; -import type { Meta, Story } from '@storybook/react'; +import React, { useContext } from 'react'; +import type { Meta, StoryFn } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; import type { Props } from './AddUserToAnotherGroupModal'; import enMessages from '../../_locales/en/messages.json'; @@ -19,28 +20,25 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/AddUserToAnotherGroupModal', component: AddUserToAnotherGroupModal, - argTypes: { - candidateConversations: { - defaultValue: Array.from(Array(100), () => getDefaultGroup()), - }, - contact: { - defaultValue: getDefaultConversation(), - }, - i18n: { - defaultValue: i18n, - }, - addMembersToGroup: { action: true }, - toggleAddUserToAnotherGroupModal: { action: true }, + args: { + i18n, + candidateConversations: Array.from(Array(100), () => getDefaultGroup()), + contact: getDefaultConversation(), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ( - -); +const Template: StoryFn = args => { + return ( + + ); +}; export const Modal = Template.bind({}); -Modal.args = {}; diff --git a/ts/components/Alert.stories.tsx b/ts/components/Alert.stories.tsx index d590dcef9fcf..272b6af0d4e3 100644 --- a/ts/components/Alert.stories.tsx +++ b/ts/components/Alert.stories.tsx @@ -2,18 +2,18 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; +import type { PropsType } from './Alert'; import { Alert } from './Alert'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Alert', -}; +} satisfies Meta; const defaultProps = { i18n, @@ -33,10 +33,6 @@ export function TitleAndBodyAreStrings(): JSX.Element { ); } -TitleAndBodyAreStrings.story = { - name: 'Title and body are strings', -}; - export function BodyIsAReactNode(): JSX.Element { return ( ); } - -LongBodyWithTitle.story = { - name: 'Long body (with title)', -}; diff --git a/ts/components/Alert.tsx b/ts/components/Alert.tsx index c9ac714b7d6c..56eec431d7dc 100644 --- a/ts/components/Alert.tsx +++ b/ts/components/Alert.tsx @@ -9,7 +9,7 @@ import type { Theme } from '../util/theme'; import { Button } from './Button'; import { Modal } from './Modal'; -type PropsType = { +export type PropsType = { body: ReactNode; i18n: LocalizerType; onClose: () => void; diff --git a/ts/components/AnimatedEmojiGalore.stories.tsx b/ts/components/AnimatedEmojiGalore.stories.tsx index 491e2eec0343..acc4edc314cd 100644 --- a/ts/components/AnimatedEmojiGalore.stories.tsx +++ b/ts/components/AnimatedEmojiGalore.stories.tsx @@ -4,12 +4,13 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './AnimatedEmojiGalore'; import { AnimatedEmojiGalore } from './AnimatedEmojiGalore'; export default { title: 'Components/AnimatedEmojiGalore', -}; +} satisfies Meta; function getDefaultProps(): PropsType { return { diff --git a/ts/components/Avatar.stories.tsx b/ts/components/Avatar.stories.tsx index ec29c5cad142..d09181694d7a 100644 --- a/ts/components/Avatar.stories.tsx +++ b/ts/components/Avatar.stories.tsx @@ -1,13 +1,12 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import * as React from 'react'; import { action } from '@storybook/addon-actions'; -import { expect } from '@storybook/jest'; +import { expect, jest } from '@storybook/jest'; import { isBoolean } from 'lodash'; import { within, userEvent } from '@storybook/testing-library'; - import type { AvatarColorType } from '../types/Colors'; import type { Props } from './Avatar'; import enMessages from '../../_locales/en/messages.json'; @@ -42,7 +41,6 @@ export default { }, blur: { control: { type: 'radio' }, - defaultValue: undefined, options: { Undefined: undefined, NoBlur: AvatarBlur.NoBlur, @@ -51,14 +49,12 @@ export default { }, }, color: { - defaultValue: AvatarColors[0], options: colorMap, }, conversationType: { control: { type: 'radio' }, options: conversationTypeMap, }, - onClick: { action: true }, size: { control: false, }, @@ -68,11 +64,16 @@ export default { }, theme: { control: { type: 'radio' }, - defaultValue: ThemeType.light, options: ThemeType, }, }, -} as Meta; + args: { + blur: undefined, + color: AvatarColors[0], + onClick: action('onClick'), + theme: ThemeType.light, + }, +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ acceptedMessageRequest: isBoolean(overrideProps.acceptedMessageRequest) @@ -87,7 +88,7 @@ const createProps = (overrideProps: Partial = {}): Props => ({ isMe: false, loading: Boolean(overrideProps.loading), noteToSelf: Boolean(overrideProps.noteToSelf), - onClick: action('onClick'), + onClick: jest.fn(action('onClick')), onClickBadge: action('onClickBadge'), phoneNumber: overrideProps.phoneNumber || '', searchResult: Boolean(overrideProps.searchResult), @@ -103,16 +104,18 @@ const sizes = Object.values(AvatarSize).filter( ) as Array; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ( - <> - {sizes.map(size => ( - - ))} - -); +const Template: StoryFn = (args: Props) => { + return ( + <> + {sizes.map(size => ( + + ))} + + ); +}; // eslint-disable-next-line react/function-component-definition -const TemplateSingle: Story = args => ( +const TemplateSingle: StoryFn = (args: Props) => ( ); @@ -120,72 +123,50 @@ export const Default = Template.bind({}); Default.args = createProps({ avatarPath: '/fixtures/giphy-GVNvOUpeYmI7e.gif', }); -Default.play = async ({ args, canvasElement }) => { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +Default.play = async (context: any) => { + const { args, canvasElement } = context; const canvas = within(canvasElement); const [avatar] = canvas.getAllByRole('button'); await userEvent.click(avatar); await expect(args.onClick).toHaveBeenCalled(); }; -Default.story = { - name: 'Avatar', -}; export const WithBadge = Template.bind({}); WithBadge.args = createProps({ avatarPath: '/fixtures/kitten-3-64-64.jpg', badge: getFakeBadge(), }); -WithBadge.story = { - name: 'With badge', -}; export const WideImage = Template.bind({}); WideImage.args = createProps({ avatarPath: '/fixtures/wide.jpg', }); -WideImage.story = { - name: 'Wide image', -}; export const OneWordName = Template.bind({}); OneWordName.args = createProps({ title: 'John', }); -OneWordName.story = { - name: 'One-word Name', -}; export const TwoWordName = Template.bind({}); TwoWordName.args = createProps({ title: 'John Smith', }); -TwoWordName.story = { - name: 'Two-word Name', -}; export const WideInitials = Template.bind({}); WideInitials.args = createProps({ title: 'Walter White', }); -WideInitials.story = { - name: 'Wide initials', -}; export const ThreeWordName = Template.bind({}); ThreeWordName.args = createProps({ title: 'Walter H. White', }); -ThreeWordName.story = { - name: 'Three-word name', -}; export const NoteToSelf = Template.bind({}); NoteToSelf.args = createProps({ noteToSelf: true, }); -NoteToSelf.story = { - name: 'Note to Self', -}; export const ContactIcon = Template.bind({}); ContactIcon.args = createProps(); @@ -227,9 +208,6 @@ BrokenAvatarForGroup.args = createProps({ avatarPath: 'badimage.png', conversationType: 'group', }); -BrokenAvatarForGroup.story = { - name: 'Broken Avatar for Group', -}; export const Loading = Template.bind({}); Loading.args = createProps({ @@ -242,42 +220,26 @@ BlurredBasedOnProps.args = createProps({ avatarPath: '/fixtures/kitten-3-64-64.jpg', }); -BlurredBasedOnProps.story = { - name: 'Blurred based on props', -}; - export const ForceBlurred = TemplateSingle.bind({}); ForceBlurred.args = createProps({ avatarPath: '/fixtures/kitten-3-64-64.jpg', blur: AvatarBlur.BlurPicture, }); -ForceBlurred.story = { - name: 'Force-blurred', -}; export const BlurredWithClickToView = TemplateSingle.bind({}); BlurredWithClickToView.args = createProps({ avatarPath: '/fixtures/kitten-3-64-64.jpg', blur: AvatarBlur.BlurPictureWithClickToView, }); -BlurredWithClickToView.story = { - name: 'Blurred with "click to view"', -}; export const StoryUnread = TemplateSingle.bind({}); StoryUnread.args = createProps({ avatarPath: '/fixtures/kitten-3-64-64.jpg', storyRing: HasStories.Unread, }); -StoryUnread.story = { - name: 'Story: unread', -}; export const StoryRead = TemplateSingle.bind({}); StoryRead.args = createProps({ avatarPath: '/fixtures/kitten-3-64-64.jpg', storyRing: HasStories.Read, }); -StoryRead.story = { - name: 'Story: read', -}; diff --git a/ts/components/AvatarColorPicker.stories.tsx b/ts/components/AvatarColorPicker.stories.tsx index c66e745de557..43e03caa3367 100644 --- a/ts/components/AvatarColorPicker.stories.tsx +++ b/ts/components/AvatarColorPicker.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -21,7 +22,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/AvatarColorPicker', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/AvatarEditor.stories.tsx b/ts/components/AvatarEditor.stories.tsx index f4ef76f54063..6301b4c23320 100644 --- a/ts/components/AvatarEditor.stories.tsx +++ b/ts/components/AvatarEditor.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -80,7 +81,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/AvatarEditor', -}; +} satisfies Meta; export function NoAvatarGroup(): JSX.Element { return ( @@ -93,20 +94,12 @@ export function NoAvatarGroup(): JSX.Element { ); } -NoAvatarGroup.story = { - name: 'No Avatar (group)', -}; - export function NoAvatarMe(): JSX.Element { return ( ); } -NoAvatarMe.story = { - name: 'No Avatar (me)', -}; - export function HasAvatar(): JSX.Element { return ( = {}): PropsType => ({ export default { title: 'Components/AvatarIconEditor', -}; +} satisfies Meta; export function PersonalIcon(): JSX.Element { return ( diff --git a/ts/components/AvatarLightbox.stories.tsx b/ts/components/AvatarLightbox.stories.tsx index c06b7984816e..a9e5bdc70c54 100644 --- a/ts/components/AvatarLightbox.stories.tsx +++ b/ts/components/AvatarLightbox.stories.tsx @@ -2,10 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - import { action } from '@storybook/addon-actions'; -import { select } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import { AvatarColors } from '../types/Colors'; import type { PropsType } from './AvatarLightbox'; @@ -15,51 +13,37 @@ import { getDefaultConversation } from '../test-both/helpers/getDefaultConversat const i18n = setupI18n('en', enMessages); -const createProps = (overrideProps: Partial = {}): PropsType => ({ - avatarColor: select( - 'Color', - AvatarColors, - overrideProps.avatarColor || AvatarColors[0] - ), - avatarPath: overrideProps.avatarPath, - conversationTitle: overrideProps.conversationTitle, - i18n, - isGroup: Boolean(overrideProps.isGroup), - onClose: action('onClose'), -}); - export default { title: 'Components/AvatarLightbox', -}; + component: AvatarLightbox, + argTypes: { + avatarColor: { + control: { type: 'select' }, + options: AvatarColors, + }, + }, + args: { + i18n, + avatarColor: AvatarColors[0], + onClose: action('onClose'), + }, +} satisfies Meta; -export function Group(): JSX.Element { - return ( - - ); +export function Group(args: PropsType): JSX.Element { + return ; } -export function Person(): JSX.Element { +export function Person(args: PropsType): JSX.Element { const conversation = getDefaultConversation(); return ( ); } -export function Photo(): JSX.Element { - return ( - - ); +export function Photo(args: PropsType): JSX.Element { + return ; } diff --git a/ts/components/AvatarModalButtons.stories.tsx b/ts/components/AvatarModalButtons.stories.tsx index d4488f6645d9..10005459ee7b 100644 --- a/ts/components/AvatarModalButtons.stories.tsx +++ b/ts/components/AvatarModalButtons.stories.tsx @@ -5,6 +5,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import type { PropsType } from './AvatarModalButtons'; import { AvatarModalButtons } from './AvatarModalButtons'; @@ -21,7 +22,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/AvatarModalButtons', -}; +} satisfies Meta; export function HasChanges(): JSX.Element { return ( @@ -33,14 +34,6 @@ export function HasChanges(): JSX.Element { ); } -HasChanges.story = { - name: 'Has changes', -}; - export function NoChanges(): JSX.Element { return ; } - -NoChanges.story = { - name: 'No changes', -}; diff --git a/ts/components/AvatarPreview.stories.tsx b/ts/components/AvatarPreview.stories.tsx index 3868ef0c40b7..bedde737a8d4 100644 --- a/ts/components/AvatarPreview.stories.tsx +++ b/ts/components/AvatarPreview.stories.tsx @@ -6,6 +6,7 @@ import { chunk } from 'lodash'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './AvatarPreview'; import { AvatarPreview } from './AvatarPreview'; import { AvatarColors } from '../types/Colors'; @@ -37,7 +38,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/AvatarPreview', -}; +} satisfies Meta; export function NoStatePersonal(): JSX.Element { return ( @@ -50,10 +51,6 @@ export function NoStatePersonal(): JSX.Element { ); } -NoStatePersonal.story = { - name: 'No state (personal)', -}; - export function NoStateGroup(): JSX.Element { return ( ; } -Value.story = { - name: 'value', -}; - export function Path(): JSX.Element { return ( ); } - -Style.story = { - name: 'style', -}; diff --git a/ts/components/AvatarTextEditor.stories.tsx b/ts/components/AvatarTextEditor.stories.tsx index 3d113be24806..8058bb77640f 100644 --- a/ts/components/AvatarTextEditor.stories.tsx +++ b/ts/components/AvatarTextEditor.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -22,7 +23,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/AvatarTextEditor', -}; +} satisfies Meta; export function Empty(): JSX.Element { return ; @@ -42,10 +43,6 @@ export function WithData(): JSX.Element { ); } -WithData.story = { - name: 'with Data', -}; - export function WithWideCharacters(): JSX.Element { return ( ); } - -WithWideCharacters.story = { - name: 'with wide characters', -}; diff --git a/ts/components/AvatarUploadButton.stories.tsx b/ts/components/AvatarUploadButton.stories.tsx index 1f4aed996b5a..6a866de04861 100644 --- a/ts/components/AvatarUploadButton.stories.tsx +++ b/ts/components/AvatarUploadButton.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -20,7 +21,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/AvatarUploadButton', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/BadgeDescription.stories.tsx b/ts/components/BadgeDescription.stories.tsx index 5edb8e2d518d..fe9b1a8f14bd 100644 --- a/ts/components/BadgeDescription.stories.tsx +++ b/ts/components/BadgeDescription.stories.tsx @@ -3,11 +3,13 @@ import React from 'react'; +import type { Meta } from '@storybook/react'; +import type { Props } from './BadgeDescription'; import { BadgeDescription } from './BadgeDescription'; export default { title: 'Components/BadgeDescription', -}; +} satisfies Meta; export function NormalName(): JSX.Element { return ( @@ -19,11 +21,7 @@ export function NormalName(): JSX.Element { ); } -NormalName.story = { - name: 'Normal name', -}; - -export function NameWithRtlOverrides(): JSX.Element { +export function NameWithRTLOverrides(): JSX.Element { return ( ); } - -NameWithRtlOverrides.story = { - name: 'Name with RTL overrides', -}; diff --git a/ts/components/BadgeDescription.tsx b/ts/components/BadgeDescription.tsx index e5343fa92735..5fee7f4a0269 100644 --- a/ts/components/BadgeDescription.tsx +++ b/ts/components/BadgeDescription.tsx @@ -5,15 +5,17 @@ import type { ReactChild, ReactElement } from 'react'; import React from 'react'; import { ContactName } from './conversation/ContactName'; +export type Props = Readonly<{ + firstName?: string; + template: string; + title: string; +}>; + export function BadgeDescription({ firstName, template, title, -}: Readonly<{ - firstName?: string; - template: string; - title: string; -}>): ReactElement { +}: Props): ReactElement { const result: Array = []; let lastIndex = 0; diff --git a/ts/components/BadgeDialog.stories.tsx b/ts/components/BadgeDialog.stories.tsx index 36ff3b2dccd0..c45deeea0b74 100644 --- a/ts/components/BadgeDialog.stories.tsx +++ b/ts/components/BadgeDialog.stories.tsx @@ -5,18 +5,20 @@ import type { ComponentProps } from 'react'; import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; import { getFakeBadge, getFakeBadges } from '../test-both/helpers/getFakeBadge'; import { repeat, zipObject } from '../util/iterables'; import { BadgeImageTheme } from '../badges/BadgeImageTheme'; +import type { PropsType } from './BadgeDialog'; import { BadgeDialog } from './BadgeDialog'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/BadgeDialog', -}; +} satisfies Meta; const defaultProps: ComponentProps = { areWeASubscriber: false, @@ -31,18 +33,10 @@ export function NoBadgesClosedImmediately(): JSX.Element { return ; } -NoBadgesClosedImmediately.story = { - name: 'No badges (closed immediately)', -}; - export function OneBadge(): JSX.Element { return ; } -OneBadge.story = { - name: 'One badge', -}; - export function BadgeWithNoImageShouldBeImpossible(): JSX.Element { return ( ; } -FiveBadges.story = { - name: 'Five badges', -}; - export function ManyBadges(): JSX.Element { return ; } -ManyBadges.story = { - name: 'Many badges', -}; - export function ManyBadgesUserIsASubscriber(): JSX.Element { return ( ); } - -ManyBadgesUserIsASubscriber.story = { - name: 'Many badges, user is a subscriber', -}; diff --git a/ts/components/BadgeDialog.tsx b/ts/components/BadgeDialog.tsx index 2224d6e76175..fafc7c7b4944 100644 --- a/ts/components/BadgeDialog.tsx +++ b/ts/components/BadgeDialog.tsx @@ -15,7 +15,7 @@ import { BadgeImage } from './BadgeImage'; import { BadgeCarouselIndex } from './BadgeCarouselIndex'; import { BadgeSustainerInstructionsDialog } from './BadgeSustainerInstructionsDialog'; -type PropsType = Readonly<{ +export type PropsType = Readonly<{ areWeASubscriber: boolean; badges: ReadonlyArray; firstName?: string; diff --git a/ts/components/BetterAvatar.stories.tsx b/ts/components/BetterAvatar.stories.tsx index e07cae81c426..6a15a926f635 100644 --- a/ts/components/BetterAvatar.stories.tsx +++ b/ts/components/BetterAvatar.stories.tsx @@ -5,6 +5,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import { AvatarColors } from '../types/Colors'; import { GroupAvatarIcons, PersonalAvatarIcons } from '../types/Avatar'; @@ -28,7 +29,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/BetterAvatar', -}; +} satisfies Meta; export function Text(): JSX.Element { return ( diff --git a/ts/components/BetterAvatarBubble.stories.tsx b/ts/components/BetterAvatarBubble.stories.tsx index b4921bc95d33..c4fa5f1ded41 100644 --- a/ts/components/BetterAvatarBubble.stories.tsx +++ b/ts/components/BetterAvatarBubble.stories.tsx @@ -5,6 +5,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import { AvatarColors } from '../types/Colors'; import type { PropsType } from './BetterAvatarBubble'; @@ -25,7 +26,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/BetterAvatarBubble', -}; +} satisfies Meta; export function Children(): JSX.Element { return ( diff --git a/ts/components/Button.stories.tsx b/ts/components/Button.stories.tsx index 7ad08a7f3eb1..c745533933ea 100644 --- a/ts/components/Button.stories.tsx +++ b/ts/components/Button.stories.tsx @@ -3,12 +3,13 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './Button'; import { Button, ButtonSize, ButtonVariant } from './Button'; export default { title: 'Components/Button', -}; +} satisfies Meta; export function KitchenSink(): JSX.Element { return ( @@ -44,10 +45,6 @@ export function KitchenSink(): JSX.Element { ); } -KitchenSink.story = { - name: 'Kitchen sink', -}; - export function AriaLabel(): JSX.Element { return ( ); } - -CustomStyles.story = { - name: 'Custom styles', -}; diff --git a/ts/components/Button.tsx b/ts/components/Button.tsx index ce39c7bb45f3..36dee96f5001 100644 --- a/ts/components/Button.tsx +++ b/ts/components/Button.tsx @@ -40,7 +40,7 @@ export enum ButtonIconType { video = 'video', } -type PropsType = { +export type PropsType = { className?: string; disabled?: boolean; icon?: ButtonIconType; diff --git a/ts/components/CallManager.stories.tsx b/ts/components/CallManager.stories.tsx index 4a176796a3d7..a9d43496c3bf 100644 --- a/ts/components/CallManager.stories.tsx +++ b/ts/components/CallManager.stories.tsx @@ -3,8 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; -import { boolean, select, text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './CallManager'; import { CallManager } from './CallManager'; import { @@ -16,7 +15,6 @@ import { GroupCallJoinState, } from '../types/Calling'; import type { ConversationTypeType } from '../state/ducks/conversations'; -import type { AvatarColorType } from '../types/Colors'; import { AvatarColors } from '../types/Colors'; import { generateAci } from '../types/ServiceId'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; @@ -33,13 +31,9 @@ const getConversation = () => getDefaultConversation({ id: '3051234567', avatarPath: undefined, - color: select( - 'Callee color', - AvatarColors, - 'ultramarine' as AvatarColorType - ), - title: text('Callee Title', 'Rick Sanchez'), - name: text('Callee Name', 'Rick Sanchez'), + color: AvatarColors[0], + title: 'Rick Sanchez', + name: 'Rick Sanchez', phoneNumber: '3051234567', profileName: 'Rick Sanchez', markedUnread: false, @@ -50,18 +44,14 @@ const getConversation = () => const getCommonActiveCallData = () => ({ conversation: getConversation(), joinedAt: Date.now(), - hasLocalAudio: boolean('hasLocalAudio', true), - hasLocalVideo: boolean('hasLocalVideo', false), - localAudioLevel: select('localAudioLevel', [0, 0.5, 1], 0), - viewMode: select( - 'viewMode', - [CallViewMode.Grid, CallViewMode.Presentation, CallViewMode.Speaker], - CallViewMode.Grid - ), - outgoingRing: boolean('outgoingRing', true), - pip: boolean('pip', false), - settingsDialogOpen: boolean('settingsDialogOpen', false), - showParticipantsList: boolean('showParticipantsList', false), + hasLocalAudio: true, + hasLocalVideo: false, + localAudioLevel: 0, + viewMode: CallViewMode.Grid, + outgoingRing: true, + pip: false, + settingsDialogOpen: false, + showParticipantsList: false, }); const createProps = (storyProps: Partial = {}): PropsType => ({ @@ -83,12 +73,8 @@ const createProps = (storyProps: Partial = {}): PropsType => ({ keyChangeOk: action('key-change-ok'), me: { ...getDefaultConversation({ - color: select( - 'Caller color', - AvatarColors, - 'ultramarine' as AvatarColorType - ), - title: text('Caller Title', 'Morty Smith'), + color: AvatarColors[0], + title: 'Morty Smith', }), serviceId: generateAci(), }, @@ -123,7 +109,9 @@ const createProps = (storyProps: Partial = {}): PropsType => ({ export default { title: 'Components/CallManager', -}; + argTypes: {}, + args: {}, +} satisfies Meta; export function NoCall(): JSX.Element { return ; @@ -184,10 +172,6 @@ export function RingingDirectCall(): JSX.Element { ); } -RingingDirectCall.story = { - name: 'Ringing (direct call)', -}; - export function RingingGroupCall(): JSX.Element { return ( ); } - -GroupCallSafetyNumberChanged.story = { - name: 'Group call - Safety Number Changed', -}; diff --git a/ts/components/CallScreen.stories.tsx b/ts/components/CallScreen.stories.tsx index 1af74d0cc90f..06e699243d07 100644 --- a/ts/components/CallScreen.stories.tsx +++ b/ts/components/CallScreen.stories.tsx @@ -3,9 +3,9 @@ import * as React from 'react'; import { times } from 'lodash'; -import { boolean, select, number } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { GroupCallRemoteParticipantType } from '../types/Calling'; import { CallMode, @@ -60,6 +60,7 @@ type GroupCallOverrideProps = OverridePropsBase & { connectionState?: GroupCallConnectionState; peekedParticipants?: Array; remoteParticipants?: Array; + remoteAudioLevel?: number; }; const createActiveDirectCallProp = ( @@ -67,18 +68,11 @@ const createActiveDirectCallProp = ( ) => ({ callMode: CallMode.Direct as CallMode.Direct, conversation, - callState: select( - 'callState', - CallState, - overrideProps.callState || CallState.Accepted - ), + callState: overrideProps.callState ?? CallState.Accepted, peekedParticipants: [] as [], remoteParticipants: [ { - hasRemoteVideo: boolean( - 'hasRemoteVideo', - Boolean(overrideProps.hasRemoteVideo) - ), + hasRemoteVideo: overrideProps.hasRemoteVideo ?? false, presenting: false, title: 'test', }, @@ -109,12 +103,7 @@ const createActiveGroupCallProp = (overrideProps: GroupCallOverrideProps) => ({ remoteAudioLevels: new Map( overrideProps.remoteParticipants?.map((_participant, index) => [ index, - number('remoteAudioLevel', 0, { - range: true, - min: 0, - max: 1, - step: 0.01, - }), + overrideProps.remoteAudioLevel ?? 0, ]) ), }); @@ -125,24 +114,10 @@ const createActiveCallProp = ( const baseResult = { joinedAt: Date.now(), conversation, - hasLocalAudio: boolean( - 'hasLocalAudio', - overrideProps.hasLocalAudio || false - ), - hasLocalVideo: boolean( - 'hasLocalVideo', - overrideProps.hasLocalVideo || false - ), - localAudioLevel: select( - 'localAudioLevel', - [0, 0.5, 1], - overrideProps.localAudioLevel || 0 - ), - viewMode: select( - 'viewMode', - [CallViewMode.Grid, CallViewMode.Speaker, CallViewMode.Presentation], - overrideProps.viewMode || CallViewMode.Grid - ), + hasLocalAudio: overrideProps.hasLocalAudio ?? false, + hasLocalVideo: overrideProps.hasLocalVideo ?? false, + localAudioLevel: overrideProps.localAudioLevel ?? 0, + viewMode: overrideProps.viewMode ?? CallViewMode.Grid, outgoingRing: true, pip: false, settingsDialogOpen: false, @@ -184,7 +159,7 @@ const createProps = ( setLocalVideo: action('set-local-video'), setPresenting: action('toggle-presenting'), setRendererCanvas: action('set-renderer-canvas'), - stickyControls: boolean('stickyControls', false), + stickyControls: false, switchToPresentationView: action('switch-to-presentation-view'), switchFromPresentationView: action('switch-from-presentation-view'), toggleParticipants: action('toggle-participants'), @@ -198,7 +173,9 @@ const createProps = ( export default { title: 'Components/CallScreen', -}; + argTypes: {}, + args: {}, +} satisfies Meta; export function Default(): JSX.Element { return ; @@ -215,11 +192,7 @@ export function PreRing(): JSX.Element { ); } -PreRing.story = { - name: 'Pre-Ring', -}; - -export const _Ringing = (): JSX.Element => { +export function Ringing(): JSX.Element { return ( { })} /> ); -}; +} -export const _Reconnecting = (): JSX.Element => { +export function Reconnecting(): JSX.Element { return ( { })} /> ); -}; +} -export const _Ended = (): JSX.Element => { +export function Ended(): JSX.Element { return ( { })} /> ); -}; +} export function HasLocalAudio(): JSX.Element { return ( @@ -263,10 +236,6 @@ export function HasLocalAudio(): JSX.Element { ); } -HasLocalAudio.story = { - name: 'hasLocalAudio', -}; - export function HasLocalVideo(): JSX.Element { return ( ({ aci: generateAci(), @@ -347,24 +304,12 @@ export function GroupCallMany(): JSX.Element { ); } -GroupCallMany.story = { - name: 'Group call - Many', -}; - export function GroupCallReconnecting(): JSX.Element { return ( ); } - -GroupCallSomeoneIsSharingScreenAndYoureReconnecting.story = { - name: "Group call - someone is sharing screen and you're reconnecting", -}; diff --git a/ts/components/CallingAudioIndicator.stories.tsx b/ts/components/CallingAudioIndicator.stories.tsx index ae0f02ba7aa3..5090a4eaf38e 100644 --- a/ts/components/CallingAudioIndicator.stories.tsx +++ b/ts/components/CallingAudioIndicator.stories.tsx @@ -2,8 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState, useEffect } from 'react'; -import { boolean } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; +import type { Props } from './CallingAudioIndicator'; import { CallingAudioIndicator, SPEAKING_LINGER_MS, @@ -13,9 +13,18 @@ import { useValueAtFixedRate } from '../hooks/useValueAtFixedRate'; export default { title: 'Components/CallingAudioIndicator', -}; + component: CallingAudioIndicator, + argTypes: { + hasAudio: { + control: { type: 'boolean' }, + }, + }, + args: { + hasAudio: true, + }, +} satisfies Meta; -export function Extreme(): JSX.Element { +export function Extreme(args: Props): JSX.Element { const [audioLevel, setAudioLevel] = useState(1); useEffect(() => { @@ -32,14 +41,14 @@ export function Extreme(): JSX.Element { return ( ); } -export function Random(): JSX.Element { +export function Random(args: Props): JSX.Element { const [audioLevel, setAudioLevel] = useState(1); useEffect(() => { @@ -56,7 +65,7 @@ export function Random(): JSX.Element { return ( diff --git a/ts/components/CallingAudioIndicator.tsx b/ts/components/CallingAudioIndicator.tsx index 4a0eb5b193cb..f3d1266d6770 100644 --- a/ts/components/CallingAudioIndicator.tsx +++ b/ts/components/CallingAudioIndicator.tsx @@ -99,15 +99,17 @@ function Bars({ audioLevel }: { audioLevel: number }): ReactElement { ); } +export type Props = Readonly<{ + hasAudio: boolean; + audioLevel: number; + shouldShowSpeaking: boolean; +}>; + export function CallingAudioIndicator({ hasAudio, audioLevel, shouldShowSpeaking, -}: Readonly<{ - hasAudio: boolean; - audioLevel: number; - shouldShowSpeaking: boolean; -}>): ReactElement { +}: Props): ReactElement { if (!hasAudio) { return (

= {}): PropsType => ({ - buttonType: - overrideProps.buttonType || - select('buttonType', CallingButtonType, CallingButtonType.HANG_UP), - i18n, - onClick: action('on-click'), - onMouseEnter: action('on-mouse-enter'), - onMouseLeave: action('on-mouse-leave'), - tooltipDirection: select( - 'tooltipDirection', - TooltipPlacement, - overrideProps.tooltipDirection || TooltipPlacement.Bottom - ), -}); - export default { title: 'Components/CallingButton', -}; + component: CallingButton, + argTypes: { + buttonType: { + control: { type: 'select' }, + options: Object.values(CallingButtonType), + }, + tooltipDirection: { + control: { type: 'select' }, + options: Object.values(TooltipPlacement), + }, + }, + args: { + buttonType: CallingButtonType.HANG_UP, + i18n, + onClick: action('on-click'), + onMouseEnter: action('on-mouse-enter'), + onMouseLeave: action('on-mouse-leave'), + tooltipDirection: TooltipPlacement.Bottom, + }, +} satisfies Meta; -export function KitchenSink(): JSX.Element { +export function KitchenSink(args: PropsType): JSX.Element { return ( <> - {Object.keys(CallingButtonType).map(buttonType => ( - + {Object.values(CallingButtonType).map(buttonType => ( + ))} ); } -export function AudioOn(): JSX.Element { - const props = createProps({ - buttonType: CallingButtonType.AUDIO_ON, - }); - return ; +export function AudioOn(args: PropsType): JSX.Element { + return ; } -export function AudioOff(): JSX.Element { - const props = createProps({ - buttonType: CallingButtonType.AUDIO_OFF, - }); - return ; +export function AudioOff(args: PropsType): JSX.Element { + return ; } -export function AudioDisabled(): JSX.Element { - const props = createProps({ - buttonType: CallingButtonType.AUDIO_DISABLED, - }); - return ; +export function AudioDisabled(args: PropsType): JSX.Element { + return ( + + ); } -export function VideoOn(): JSX.Element { - const props = createProps({ - buttonType: CallingButtonType.VIDEO_ON, - }); - return ; +export function VideoOn(args: PropsType): JSX.Element { + return ; } -export function VideoOff(): JSX.Element { - const props = createProps({ - buttonType: CallingButtonType.VIDEO_OFF, - }); - return ; +export function VideoOff(args: PropsType): JSX.Element { + return ; } -export function VideoDisabled(): JSX.Element { - const props = createProps({ - buttonType: CallingButtonType.VIDEO_DISABLED, - }); - return ; +export function VideoDisabled(args: PropsType): JSX.Element { + return ( + + ); } -export function TooltipRight(): JSX.Element { - const props = createProps({ - tooltipDirection: TooltipPlacement.Right, - }); - return ; +export function TooltipRight(args: PropsType): JSX.Element { + return ; } -TooltipRight.story = { - name: 'Tooltip right', -}; - -export function PresentingOn(): JSX.Element { - const props = createProps({ - buttonType: CallingButtonType.PRESENTING_ON, - }); - return ; +export function PresentingOn(args: PropsType): JSX.Element { + return ( + + ); } -export function PresentingOff(): JSX.Element { - const props = createProps({ - buttonType: CallingButtonType.PRESENTING_OFF, - }); - return ; +export function PresentingOff(args: PropsType): JSX.Element { + return ( + + ); } diff --git a/ts/components/CallingDeviceSelection.stories.tsx b/ts/components/CallingDeviceSelection.stories.tsx index 0c2935c3893e..c75828e1c6fd 100644 --- a/ts/components/CallingDeviceSelection.stories.tsx +++ b/ts/components/CallingDeviceSelection.stories.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { Props } from './CallingDeviceSelection'; import { CallingDeviceSelection } from './CallingDeviceSelection'; import { setupI18n } from '../util/setupI18n'; @@ -39,7 +40,7 @@ const createProps = ({ export default { title: 'Components/CallingDeviceSelection', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/CallingHeader.stories.tsx b/ts/components/CallingHeader.stories.tsx index e00400db2a1c..45e0275fb240 100644 --- a/ts/components/CallingHeader.stories.tsx +++ b/ts/components/CallingHeader.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { boolean, number } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './CallingHeader'; import { CallingHeader } from './CallingHeader'; import { setupI18n } from '../util/setupI18n'; @@ -12,36 +11,35 @@ import enMessages from '../../_locales/en/messages.json'; const i18n = setupI18n('en', enMessages); -const createProps = (overrideProps: Partial = {}): PropsType => ({ - i18n, - isGroupCall: boolean('isGroupCall', Boolean(overrideProps.isGroupCall)), - message: overrideProps.message, - participantCount: number( - 'participantCount', - overrideProps.participantCount || 0 - ), - showParticipantsList: boolean( - 'showParticipantsList', - Boolean(overrideProps.showParticipantsList) - ), - title: overrideProps.title || 'With Someone', - toggleParticipants: () => action('toggle-participants'), - togglePip: () => action('toggle-pip'), - toggleSettings: () => action('toggle-settings'), -}); - export default { title: 'Components/CallingHeader', -}; + component: CallingHeader, + argTypes: { + isGroupCall: { control: { type: 'boolean' } }, + participantCount: { control: { type: 'number' } }, + title: { control: { type: 'text' } }, + }, + args: { + i18n, + isGroupCall: false, + message: '', + participantCount: 0, + showParticipantsList: false, + title: 'With Someone', + toggleParticipants: action('toggle-participants'), + togglePip: action('toggle-pip'), + toggleSettings: action('toggle-settings'), + }, +} satisfies Meta; -export function Default(): JSX.Element { - return ; +export function Default(args: PropsType): JSX.Element { + return ; } -export function LobbyStyle(): JSX.Element { +export function LobbyStyle(args: PropsType): JSX.Element { return ( ; +} -export function WithParticipants(): JSX.Element { +export function WithParticipantsShown(args: PropsType): JSX.Element { return ( ); } -export function WithParticipantsShown(): JSX.Element { +export function LongTitle(args: PropsType): JSX.Element { return ( ); } -WithParticipantsShown.story = { - name: 'With Participants (shown)', -}; - -export function LongTitle(): JSX.Element { +export function TitleWithMessage(args: PropsType): JSX.Element { return ( - + ); } - -export function TitleWithMessage(): JSX.Element { - return ( - - ); -} - -TitleWithMessage.story = { - name: 'Title with message', -}; diff --git a/ts/components/CallingLobby.stories.tsx b/ts/components/CallingLobby.stories.tsx index 645218b6d967..adff3383014d 100644 --- a/ts/components/CallingLobby.stories.tsx +++ b/ts/components/CallingLobby.stories.tsx @@ -3,10 +3,10 @@ import * as React from 'react'; import { times } from 'lodash'; -import { boolean } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; import { v4 as generateUuid } from 'uuid'; +import type { Meta } from '@storybook/react'; import { AvatarColors } from '../types/Colors'; import type { ConversationType } from '../state/ducks/conversations'; import type { PropsType } from './CallingLobby'; @@ -32,10 +32,7 @@ const camera = { }; const createProps = (overrideProps: Partial = {}): PropsType => { - const isGroupCall = boolean( - 'isGroupCall', - overrideProps.isGroupCall || false - ); + const isGroupCall = overrideProps.isGroupCall ?? false; const conversation = isGroupCall ? getDefaultConversation({ title: 'Tahoe Trip', @@ -49,19 +46,13 @@ const createProps = (overrideProps: Partial = {}): PropsType => { groupMembers: overrideProps.groupMembers || (isGroupCall ? times(3, () => getDefaultConversation()) : undefined), - hasLocalAudio: boolean( - 'hasLocalAudio', - overrideProps.hasLocalAudio ?? true - ), - hasLocalVideo: boolean( - 'hasLocalVideo', - overrideProps.hasLocalVideo ?? false - ), + hasLocalAudio: overrideProps.hasLocalAudio ?? true, + hasLocalVideo: overrideProps.hasLocalVideo ?? false, i18n, isGroupCall, isGroupCallOutboundRingEnabled: true, isConversationTooBigToRing: false, - isCallFull: boolean('isCallFull', overrideProps.isCallFull || false), + isCallFull: overrideProps.isCallFull ?? false, me: overrideProps.me || getDefaultConversation({ @@ -71,16 +62,13 @@ const createProps = (overrideProps: Partial = {}): PropsType => { }), onCallCanceled: action('on-call-canceled'), onJoinCall: action('on-join-call'), - outgoingRing: boolean('outgoingRing', Boolean(overrideProps.outgoingRing)), + outgoingRing: overrideProps.outgoingRing ?? false, peekedParticipants: overrideProps.peekedParticipants || [], setLocalAudio: action('set-local-audio'), setLocalPreview: action('set-local-preview'), setLocalVideo: action('set-local-video'), setOutgoingRing: action('set-outgoing-ring'), - showParticipantsList: boolean( - 'showParticipantsList', - Boolean(overrideProps.showParticipantsList) - ), + showParticipantsList: overrideProps.showParticipantsList ?? false, toggleParticipants: action('toggle-participants'), toggleSettings: action('toggle-settings'), }; @@ -93,7 +81,9 @@ const fakePeekedParticipant = (conversationProps: Partial) => export default { title: 'Components/CallingLobby', -}; + argTypes: {}, + args: {}, +} satisfies Meta; export function Default(): JSX.Element { const props = createProps(); @@ -107,10 +97,6 @@ export function NoCameraNoAvatar(): JSX.Element { return ; } -NoCameraNoAvatar.story = { - name: 'No Camera, no avatar', -}; - export function NoCameraLocalAvatar(): JSX.Element { const props = createProps({ availableCameras: [], @@ -124,10 +110,6 @@ export function NoCameraLocalAvatar(): JSX.Element { return ; } -NoCameraLocalAvatar.story = { - name: 'No Camera, local avatar', -}; - export function LocalVideo(): JSX.Element { const props = createProps({ hasLocalVideo: true, @@ -142,20 +124,12 @@ export function InitiallyMuted(): JSX.Element { return ; } -InitiallyMuted.story = { - name: 'Initially muted', -}; - -export function GroupCall0PeekedParticipants(): JSX.Element { +export function GroupCallWithNoPeekedParticipants(): JSX.Element { const props = createProps({ isGroupCall: true, peekedParticipants: [] }); return ; } -GroupCall0PeekedParticipants.story = { - name: 'Group Call - 0 peeked participants', -}; - -export function GroupCall1PeekedParticipant(): JSX.Element { +export function GroupCallWith1PeekedParticipant(): JSX.Element { const props = createProps({ isGroupCall: true, peekedParticipants: [{ title: 'Sam' }].map(fakePeekedParticipant), @@ -163,11 +137,7 @@ export function GroupCall1PeekedParticipant(): JSX.Element { return ; } -GroupCall1PeekedParticipant.story = { - name: 'Group Call - 1 peeked participant', -}; - -export function GroupCall1PeekedParticipantSelf(): JSX.Element { +export function GroupCallWith1PeekedParticipantSelf(): JSX.Element { const serviceId = generateAci(); const props = createProps({ isGroupCall: true, @@ -180,11 +150,7 @@ export function GroupCall1PeekedParticipantSelf(): JSX.Element { return ; } -GroupCall1PeekedParticipantSelf.story = { - name: 'Group Call - 1 peeked participant (self)', -}; - -export function GroupCall4PeekedParticipants(): JSX.Element { +export function GroupCallWith4PeekedParticipants(): JSX.Element { const props = createProps({ isGroupCall: true, peekedParticipants: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'].map(title => @@ -194,11 +160,7 @@ export function GroupCall4PeekedParticipants(): JSX.Element { return ; } -GroupCall4PeekedParticipants.story = { - name: 'Group Call - 4 peeked participants', -}; - -export function GroupCall4PeekedParticipantsParticipantsList(): JSX.Element { +export function GroupCallWith4PeekedParticipantsParticipantsList(): JSX.Element { const props = createProps({ isGroupCall: true, peekedParticipants: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'].map(title => @@ -209,11 +171,7 @@ export function GroupCall4PeekedParticipantsParticipantsList(): JSX.Element { return ; } -GroupCall4PeekedParticipantsParticipantsList.story = { - name: 'Group Call - 4 peeked participants (participants list)', -}; - -export function GroupCallCallFull(): JSX.Element { +export function GroupCallWithCallFull(): JSX.Element { const props = createProps({ isGroupCall: true, isCallFull: true, @@ -224,18 +182,10 @@ export function GroupCallCallFull(): JSX.Element { return ; } -GroupCallCallFull.story = { - name: 'Group Call - call full', -}; - -export function GroupCall0PeekedParticipantsBigGroup(): JSX.Element { +export function GroupCallWith0PeekedParticipantsBigGroup(): JSX.Element { const props = createProps({ isGroupCall: true, groupMembers: times(100, () => getDefaultConversation()), }); return ; } - -GroupCall0PeekedParticipantsBigGroup.story = { - name: 'Group Call - 0 peeked participants, big group', -}; diff --git a/ts/components/CallingParticipantsList.stories.tsx b/ts/components/CallingParticipantsList.stories.tsx index 683932523346..e048994c8711 100644 --- a/ts/components/CallingParticipantsList.stories.tsx +++ b/ts/components/CallingParticipantsList.stories.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { sample } from 'lodash'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './CallingParticipantsList'; import { CallingParticipantsList } from './CallingParticipantsList'; import { AvatarColors } from '../types/Colors'; @@ -47,17 +48,13 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/CallingParticipantsList', -}; +} satisfies Meta; export function NoOne(): JSX.Element { const props = createProps(); return ; } -NoOne.story = { - name: 'No one', -}; - export function SoloCall(): JSX.Element { const props = createProps({ participants: [ diff --git a/ts/components/CallingPip.stories.tsx b/ts/components/CallingPip.stories.tsx index 1049e597a09b..2cfc22afc154 100644 --- a/ts/components/CallingPip.stories.tsx +++ b/ts/components/CallingPip.stories.tsx @@ -3,9 +3,8 @@ import * as React from 'react'; import { times } from 'lodash'; -import { boolean, select } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { AvatarColors } from '../types/Colors'; import type { ConversationType } from '../state/ducks/conversations'; import type { PropsType } from './CallingPip'; @@ -35,16 +34,19 @@ const conversation: ConversationType = getDefaultConversation({ profileName: 'Rick Sanchez', }); -const getCommonActiveCallData = () => ({ +type Overrides = { + hasLocalAudio?: boolean; + hasLocalVideo?: boolean; + localAudioLevel?: number; + viewMode?: CallViewMode; +}; + +const getCommonActiveCallData = (overrides: Overrides) => ({ conversation, - hasLocalAudio: boolean('hasLocalAudio', true), - hasLocalVideo: boolean('hasLocalVideo', false), - localAudioLevel: select('localAudioLevel', [0, 0.5, 1], 0), - viewMode: select( - 'viewMode', - [CallViewMode.Grid, CallViewMode.Speaker, CallViewMode.Presentation], - CallViewMode.Grid - ), + hasLocalAudio: overrides.hasLocalAudio ?? true, + hasLocalVideo: overrides.hasLocalVideo ?? false, + localAudioLevel: overrides.localAudioLevel ?? 0, + viewMode: overrides.viewMode ?? CallViewMode.Grid, joinedAt: Date.now(), outgoingRing: true, pip: true, @@ -52,92 +54,93 @@ const getCommonActiveCallData = () => ({ showParticipantsList: false, }); -const defaultCall: ActiveDirectCallType = { - ...getCommonActiveCallData(), - callMode: CallMode.Direct as CallMode.Direct, - callState: CallState.Accepted, - peekedParticipants: [], - remoteParticipants: [ - { hasRemoteVideo: true, presenting: false, title: 'Arsene' }, - ], +const getDefaultCall = (overrides: Overrides): ActiveDirectCallType => { + return { + ...getCommonActiveCallData(overrides), + callMode: CallMode.Direct as CallMode.Direct, + callState: CallState.Accepted, + peekedParticipants: [], + remoteParticipants: [ + { hasRemoteVideo: true, presenting: false, title: 'Arsene' }, + ], + }; }; -const createProps = (overrideProps: Partial = {}): PropsType => ({ - activeCall: overrideProps.activeCall || defaultCall, - getGroupCallVideoFrameSource: fakeGetGroupCallVideoFrameSource, - hangUpActiveCall: action('hang-up-active-call'), - hasLocalVideo: boolean('hasLocalVideo', overrideProps.hasLocalVideo || false), - i18n, - setGroupCallVideoRequest: action('set-group-call-video-request'), - setLocalPreview: action('set-local-preview'), - setRendererCanvas: action('set-renderer-canvas'), - switchFromPresentationView: action('switch-to-presentation-view'), - switchToPresentationView: action('switch-to-presentation-view'), - togglePip: action('toggle-pip'), -}); - export default { title: 'Components/CallingPip', -}; + argTypes: { + hasLocalVideo: { control: { type: 'boolean' } }, + }, + args: { + activeCall: getDefaultCall({}), + getGroupCallVideoFrameSource: fakeGetGroupCallVideoFrameSource, + hangUpActiveCall: action('hang-up-active-call'), + hasLocalVideo: false, + i18n, + setGroupCallVideoRequest: action('set-group-call-video-request'), + setLocalPreview: action('set-local-preview'), + setRendererCanvas: action('set-renderer-canvas'), + switchFromPresentationView: action('switch-to-presentation-view'), + switchToPresentationView: action('switch-to-presentation-view'), + togglePip: action('toggle-pip'), + }, +} satisfies Meta; -export function Default(): JSX.Element { - const props = createProps({}); - return ; +export function Default(args: PropsType): JSX.Element { + return ; } -export function ContactWithAvatarAndNoVideo(): JSX.Element { - const props = createProps({ - activeCall: { - ...defaultCall, - conversation: { - ...conversation, - avatarPath: 'https://www.fillmurray.com/64/64', - }, - remoteParticipants: [ - { hasRemoteVideo: false, presenting: false, title: 'Julian' }, - ], - }, - }); - return ; +export function ContactWithAvatarAndNoVideo(args: PropsType): JSX.Element { + return ( + + ); } -ContactWithAvatarAndNoVideo.story = { - name: 'Contact (with avatar and no video)', -}; - -export function ContactNoColor(): JSX.Element { - const props = createProps({ - activeCall: { - ...defaultCall, - conversation: { - ...conversation, - color: undefined, - }, - }, - }); - return ; +export function ContactNoColor(args: PropsType): JSX.Element { + return ( + + ); } -ContactNoColor.story = { - name: 'Contact (no color)', -}; - -export function GroupCall(): JSX.Element { - const props = createProps({ - activeCall: { - ...getCommonActiveCallData(), - callMode: CallMode.Group as CallMode.Group, - connectionState: GroupCallConnectionState.Connected, - conversationsWithSafetyNumberChanges: [], - groupMembers: times(3, () => getDefaultConversation()), - isConversationTooBigToRing: false, - joinState: GroupCallJoinState.Joined, - maxDevices: 5, - deviceCount: 0, - peekedParticipants: [], - remoteParticipants: [], - remoteAudioLevels: new Map(), - }, - }); - return ; +export function GroupCall(args: PropsType): JSX.Element { + return ( + getDefaultConversation()), + isConversationTooBigToRing: false, + joinState: GroupCallJoinState.Joined, + maxDevices: 5, + deviceCount: 0, + peekedParticipants: [], + remoteParticipants: [], + remoteAudioLevels: new Map(), + }} + /> + ); } diff --git a/ts/components/CallingPreCallInfo.stories.tsx b/ts/components/CallingPreCallInfo.stories.tsx index 4297403e6b02..c20e0d8940f1 100644 --- a/ts/components/CallingPreCallInfo.stories.tsx +++ b/ts/components/CallingPreCallInfo.stories.tsx @@ -3,10 +3,11 @@ import React from 'react'; import { times } from 'lodash'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; - +import type { PropsType } from './CallingPreCallInfo'; import { CallingPreCallInfo, RingMode } from './CallingPreCallInfo'; const i18n = setupI18n('en', enMessages); @@ -22,7 +23,7 @@ const otherMembers = times(6, () => getDefaultConversation()); export default { title: 'Components/CallingPreCallInfo', -}; +} satisfies Meta; export function DirectConversation(): JSX.Element { return ( @@ -35,10 +36,6 @@ export function DirectConversation(): JSX.Element { ); } -DirectConversation.story = { - name: 'Direct conversation', -}; - export function Ring0(): JSX.Element { return ( ); } - -GroupConversationCallIsFull.story = { - name: 'Group conversation, call is full', -}; diff --git a/ts/components/CallingPreCallInfo.tsx b/ts/components/CallingPreCallInfo.tsx index 72992f33fd48..d0be1edf92fd 100644 --- a/ts/components/CallingPreCallInfo.tsx +++ b/ts/components/CallingPreCallInfo.tsx @@ -15,7 +15,7 @@ export enum RingMode { IsRinging, } -type PropsType = { +export type PropsType = { conversation: Pick< ConversationType, | 'acceptedMessageRequest' diff --git a/ts/components/CallingScreenSharingController.stories.tsx b/ts/components/CallingScreenSharingController.stories.tsx index 76211b4887fc..08a1c30430f4 100644 --- a/ts/components/CallingScreenSharingController.stories.tsx +++ b/ts/components/CallingScreenSharingController.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './CallingScreenSharingController'; import { CallingScreenSharingController } from './CallingScreenSharingController'; @@ -21,7 +22,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/CallingScreenSharingController', -}; +} satisfies Meta; export function Controller(): JSX.Element { return ; @@ -37,7 +38,3 @@ export function ReallyLongAppName(): JSX.Element { /> ); } - -ReallyLongAppName.story = { - name: 'Really long app name', -}; diff --git a/ts/components/CallingSelectPresentingSourcesModal.stories.tsx b/ts/components/CallingSelectPresentingSourcesModal.stories.tsx index e48a88539a5e..ce7332176ec6 100644 --- a/ts/components/CallingSelectPresentingSourcesModal.stories.tsx +++ b/ts/components/CallingSelectPresentingSourcesModal.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './CallingSelectPresentingSourcesModal'; import { CallingSelectPresentingSourcesModal } from './CallingSelectPresentingSourcesModal'; @@ -55,7 +56,7 @@ const createProps = (): PropsType => ({ export default { title: 'Components/CallingSelectPresentingSourcesModal', -}; +} satisfies Meta; export function Modal(): JSX.Element { return ; diff --git a/ts/components/CaptchaDialog.stories.tsx b/ts/components/CaptchaDialog.stories.tsx index b52aefd6bead..d9071414db36 100644 --- a/ts/components/CaptchaDialog.stories.tsx +++ b/ts/components/CaptchaDialog.stories.tsx @@ -3,36 +3,33 @@ import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; -import { boolean } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './CaptchaDialog'; import { CaptchaDialog } from './CaptchaDialog'; import { Button } from './Button'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; -export default { - title: 'Components/CaptchaDialog', -}; - const i18n = setupI18n('en', enMessages); -export const _CaptchaDialog = (): JSX.Element => { +export default { + title: 'Components/CaptchaDialog', + argTypes: { + isPending: { control: { type: 'boolean' } }, + }, + args: { + i18n, + isPending: false, + onContinue: action('onContinue'), + }, +} satisfies Meta; + +export function Basic(args: PropsType): JSX.Element { const [isSkipped, setIsSkipped] = useState(false); if (isSkipped) { return ; } - return ( - setIsSkipped(true)} - /> - ); -}; - -_CaptchaDialog.story = { - name: 'CaptchaDialog', -}; + return setIsSkipped(true)} />; +} diff --git a/ts/components/CaptchaDialog.tsx b/ts/components/CaptchaDialog.tsx index 8f864f0a0dc6..0b9091960df7 100644 --- a/ts/components/CaptchaDialog.tsx +++ b/ts/components/CaptchaDialog.tsx @@ -8,7 +8,7 @@ import { Button, ButtonVariant } from './Button'; import { Modal } from './Modal'; import { Spinner } from './Spinner'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; isPending: boolean; diff --git a/ts/components/ChatColorPicker.stories.tsx b/ts/components/ChatColorPicker.stories.tsx index 597caa4e74e1..c8b534115b16 100644 --- a/ts/components/ChatColorPicker.stories.tsx +++ b/ts/components/ChatColorPicker.stories.tsx @@ -2,21 +2,45 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - import { action } from '@storybook/addon-actions'; -import { select } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import type { PropsType } from './ChatColorPicker'; import { ChatColorPicker } from './ChatColorPicker'; import { ConversationColors } from '../types/Colors'; import { setupI18n } from '../util/setupI18n'; +const i18n = setupI18n('en', enMessages); + export default { title: 'Components/ChatColorPicker', -}; - -const i18n = setupI18n('en', enMessages); + argTypes: { + selectedColor: { + control: { + type: 'select', + options: ConversationColors, + }, + }, + }, + args: { + addCustomColor: action('addCustomColor'), + colorSelected: action('colorSelected'), + editCustomColor: action('editCustomColor'), + getConversationsWithCustomColor: (_: string) => Promise.resolve([]), + i18n, + removeCustomColor: action('removeCustomColor'), + removeCustomColorOnConversations: action( + 'removeCustomColorOnConversations' + ), + resetAllChatColors: action('resetAllChatColors'), + resetDefaultChatColor: action('resetDefaultChatColor'), + selectedColor: 'basil', + selectedCustomColor: {}, + setGlobalDefaultConversationColor: action( + 'setGlobalDefaultConversationColor' + ), + }, +} satisfies Meta; const SAMPLE_CUSTOM_COLOR = { deg: 90, @@ -24,25 +48,8 @@ const SAMPLE_CUSTOM_COLOR = { start: { hue: 315, saturation: 78 }, }; -const createProps = (): PropsType => ({ - addCustomColor: action('addCustomColor'), - colorSelected: action('colorSelected'), - editCustomColor: action('editCustomColor'), - getConversationsWithCustomColor: (_: string) => Promise.resolve([]), - i18n, - removeCustomColor: action('removeCustomColor'), - removeCustomColorOnConversations: action('removeCustomColorOnConversations'), - resetAllChatColors: action('resetAllChatColors'), - resetDefaultChatColor: action('resetDefaultChatColor'), - selectedColor: select('selectedColor', ConversationColors, 'basil' as const), - selectedCustomColor: {}, - setGlobalDefaultConversationColor: action( - 'setGlobalDefaultConversationColor' - ), -}); - -export function Default(): JSX.Element { - return ; +export function Default(args: PropsType): JSX.Element { + return ; } const CUSTOM_COLORS = { @@ -62,10 +69,10 @@ const CUSTOM_COLORS = { }, }; -export function CustomColors(): JSX.Element { +export function CustomColors(args: PropsType): JSX.Element { return ( ({ export default { title: 'Components/Checkbox', -}; +} satisfies Meta; export function Normal(): JSX.Element { return ; diff --git a/ts/components/CircleCheckbox.stories.tsx b/ts/components/CircleCheckbox.stories.tsx index 0a7260251ebf..1e8b033b3f7d 100644 --- a/ts/components/CircleCheckbox.stories.tsx +++ b/ts/components/CircleCheckbox.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { Props } from './CircleCheckbox'; import { CircleCheckbox, Variant } from './CircleCheckbox'; @@ -15,7 +15,7 @@ const createProps = (): Props => ({ export default { title: 'Components/CircleCheckbox', -}; +} satisfies Meta; export function Normal(): JSX.Element { return ; diff --git a/ts/components/ClearingData.stories.tsx b/ts/components/ClearingData.stories.tsx index 3dacf136b654..e6e183f84154 100644 --- a/ts/components/ClearingData.stories.tsx +++ b/ts/components/ClearingData.stories.tsx @@ -2,23 +2,19 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; - +import type { PropsType } from './ClearingData'; import { ClearingData } from './ClearingData'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/ClearingData', -}; +} satisfies Meta; -export const _ClearingData = (): JSX.Element => ( - -); - -_ClearingData.story = { - name: 'Clearing data', -}; +export function Basic(): JSX.Element { + return ; +} diff --git a/ts/components/CompositionArea.stories.tsx b/ts/components/CompositionArea.stories.tsx index 58d414ce2850..3dde92c340a5 100644 --- a/ts/components/CompositionArea.stories.tsx +++ b/ts/components/CompositionArea.stories.tsx @@ -1,12 +1,9 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { DecoratorFunction } from '@storybook/addons'; -import * as React from 'react'; - +import React, { useContext } from 'react'; import { action } from '@storybook/addon-actions'; -import { boolean, select } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { IMAGE_JPEG } from '../types/MIME'; import type { Props } from './CompositionArea'; import { CompositionArea } from './CompositionArea'; @@ -28,287 +25,256 @@ export default { decorators: [ // necessary for the add attachment button to render properly storyFn =>
{storyFn()}
, - ] as Array>, -}; + ], + argTypes: { + recordingState: { + control: { type: 'select' }, + options: Object.keys(RecordingState), + mappings: RecordingState, + }, + messageRequestsEnabled: { control: { type: 'boolean' } }, + announcementsOnly: { control: { type: 'boolean' } }, + areWePendingApproval: { control: { type: 'boolean' } }, + }, + args: { + addAttachment: action('addAttachment'), + conversationId: '123', + convertDraftBodyRangesIntoHydrated: () => undefined, + discardEditMessage: action('discardEditMessage'), + focusCounter: 0, + sendCounter: 0, + i18n, + isDisabled: false, + isFormattingFlagEnabled: true, + isFormattingSpoilersFlagEnabled: true, + isFormattingEnabled: true, + messageCompositionId: '456', + sendEditedMessage: action('sendEditedMessage'), + sendMultiMediaMessage: action('sendMultiMediaMessage'), + platform: 'darwin', + processAttachments: action('processAttachments'), + removeAttachment: action('removeAttachment'), + setComposerFocus: action('setComposerFocus'), + setMessageToEdit: action('setMessageToEdit'), + setQuoteByMessageId: action('setQuoteByMessageId'), + showToast: action('showToast'), -const useProps = (overrideProps: Partial = {}): Props => ({ - addAttachment: action('addAttachment'), - conversationId: '123', - convertDraftBodyRangesIntoHydrated: () => undefined, - discardEditMessage: action('discardEditMessage'), - focusCounter: 0, - sendCounter: 0, - i18n, - isDisabled: false, - isFormattingFlagEnabled: - overrideProps.isFormattingFlagEnabled === false - ? overrideProps.isFormattingFlagEnabled - : true, - isFormattingSpoilersFlagEnabled: - overrideProps.isFormattingSpoilersFlagEnabled === false - ? overrideProps.isFormattingSpoilersFlagEnabled - : true, - isFormattingEnabled: - overrideProps.isFormattingEnabled === false - ? overrideProps.isFormattingEnabled - : true, - messageCompositionId: '456', - sendEditedMessage: action('sendEditedMessage'), - sendMultiMediaMessage: action('sendMultiMediaMessage'), - platform: 'darwin', - processAttachments: action('processAttachments'), - removeAttachment: action('removeAttachment'), - theme: React.useContext(StorybookThemeContext), - setComposerFocus: action('setComposerFocus'), - setMessageToEdit: action('setMessageToEdit'), - setQuoteByMessageId: action('setQuoteByMessageId'), - showToast: action('showToast'), + // AttachmentList + draftAttachments: [], + onClearAttachments: action('onClearAttachments'), + // AudioCapture + cancelRecording: action('cancelRecording'), + completeRecording: action('completeRecording'), + errorRecording: action('errorRecording'), + recordingState: RecordingState.Idle, + startRecording: action('startRecording'), + // StagedLinkPreview + linkPreviewLoading: false, + linkPreviewResult: undefined, + onCloseLinkPreview: action('onCloseLinkPreview'), + // Quote + quotedMessageProps: undefined, + scrollToMessage: action('scrollToMessage'), + // MediaEditor + imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]', + // MediaQualitySelector + setMediaQualitySetting: action('setMediaQualitySetting'), + shouldSendHighQualityAttachments: false, + // CompositionInput + onEditorStateChange: action('onEditorStateChange'), + onTextTooLong: action('onTextTooLong'), + draftText: undefined, + clearQuotedMessage: action('clearQuotedMessage'), + getPreferredBadge: () => undefined, + getQuotedMessage: action('getQuotedMessage'), + sortedGroupMembers: [], + // EmojiButton + onPickEmoji: action('onPickEmoji'), + onSetSkinTone: action('onSetSkinTone'), + recentEmojis: [], + skinTone: 1, + // StickerButton + knownPacks: [], + receivedPacks: [], + installedPacks: [], + blessedPacks: [], + recentStickers: [], + clearInstalledStickerPack: action('clearInstalledStickerPack'), + pushPanelForConversation: action('pushPanelForConversation'), + sendStickerMessage: action('sendStickerMessage'), + clearShowIntroduction: action('clearShowIntroduction'), + showPickerHint: false, + clearShowPickerHint: action('clearShowPickerHint'), + // Message Requests + conversationType: 'direct', + acceptConversation: action('acceptConversation'), + blockConversation: action('blockConversation'), + blockAndReportSpam: action('blockAndReportSpam'), + deleteConversation: action('deleteConversation'), + messageRequestsEnabled: false, + title: '', + // GroupV1 Disabled Actions + showGV2MigrationDialog: action('showGV2MigrationDialog'), + // GroupV2 + announcementsOnly: false, + areWeAdmin: false, + areWePendingApproval: false, + groupAdmins: [], + cancelJoinRequest: action('cancelJoinRequest'), + showConversation: action('showConversation'), + // SMS-only + isSMSOnly: false, + isFetchingUUID: false, + renderSmartCompositionRecording: _ =>
RECORDING
, + renderSmartCompositionRecordingDraft: _ =>
RECORDING DRAFT
, + // Select mode + selectedMessageIds: undefined, + toggleSelectMode: action('toggleSelectMode'), + toggleForwardMessagesModal: action('toggleForwardMessagesModal'), + }, +} satisfies Meta; - // AttachmentList - draftAttachments: overrideProps.draftAttachments || [], - onClearAttachments: action('onClearAttachments'), - // AudioCapture - cancelRecording: action('cancelRecording'), - completeRecording: action('completeRecording'), - errorRecording: action('errorRecording'), - recordingState: select( - 'recordingState', - RecordingState, - overrideProps.recordingState || RecordingState.Idle - ), - startRecording: action('startRecording'), - // StagedLinkPreview - linkPreviewLoading: Boolean(overrideProps.linkPreviewLoading), - linkPreviewResult: overrideProps.linkPreviewResult, - onCloseLinkPreview: action('onCloseLinkPreview'), - // Quote - quotedMessageProps: overrideProps.quotedMessageProps, - scrollToMessage: action('scrollToMessage'), - // MediaEditor - imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]', - // MediaQualitySelector - setMediaQualitySetting: action('setMediaQualitySetting'), - shouldSendHighQualityAttachments: Boolean( - overrideProps.shouldSendHighQualityAttachments - ), - // CompositionInput - onEditorStateChange: action('onEditorStateChange'), - onTextTooLong: action('onTextTooLong'), - draftText: overrideProps.draftText || undefined, - clearQuotedMessage: action('clearQuotedMessage'), - getPreferredBadge: () => undefined, - getQuotedMessage: action('getQuotedMessage'), - sortedGroupMembers: [], - // EmojiButton - onPickEmoji: action('onPickEmoji'), - onSetSkinTone: action('onSetSkinTone'), - recentEmojis: [], - skinTone: 1, - // StickerButton - knownPacks: overrideProps.knownPacks || [], - receivedPacks: [], - installedPacks: [], - blessedPacks: [], - recentStickers: [], - clearInstalledStickerPack: action('clearInstalledStickerPack'), - pushPanelForConversation: action('pushPanelForConversation'), - sendStickerMessage: action('sendStickerMessage'), - clearShowIntroduction: action('clearShowIntroduction'), - showPickerHint: false, - clearShowPickerHint: action('clearShowPickerHint'), - // Message Requests - conversationType: 'direct', - acceptConversation: action('acceptConversation'), - blockConversation: action('blockConversation'), - blockAndReportSpam: action('blockAndReportSpam'), - deleteConversation: action('deleteConversation'), - messageRequestsEnabled: boolean( - 'messageRequestsEnabled', - overrideProps.messageRequestsEnabled || false - ), - title: '', - // GroupV1 Disabled Actions - showGV2MigrationDialog: action('showGV2MigrationDialog'), - // GroupV2 - announcementsOnly: boolean( - 'announcementsOnly', - Boolean(overrideProps.announcementsOnly) - ), - areWeAdmin: boolean('areWeAdmin', Boolean(overrideProps.areWeAdmin)), - areWePendingApproval: boolean( - 'areWePendingApproval', - Boolean(overrideProps.areWePendingApproval) - ), - groupAdmins: [], - cancelJoinRequest: action('cancelJoinRequest'), - showConversation: action('showConversation'), - // SMS-only - isSMSOnly: overrideProps.isSMSOnly || false, - isFetchingUUID: overrideProps.isFetchingUUID || false, - renderSmartCompositionRecording: _ =>
RECORDING
, - renderSmartCompositionRecordingDraft: _ =>
RECORDING DRAFT
, - // Select mode - selectedMessageIds: undefined, - toggleSelectMode: action('toggleSelectMode'), - toggleForwardMessagesModal: action('toggleForwardMessagesModal'), -}); - -export function Default(): JSX.Element { - const props = useProps(); - - return ; +export function Default(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ; } -export function StartingText(): JSX.Element { - const props = useProps({ - draftText: "here's some starting text", - }); - - return ; -} - -export function StickerButton(): JSX.Element { - const props = useProps({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - knownPacks: [{} as any], - }); - - return ; -} - -export function MessageRequest(): JSX.Element { - const props = useProps({ - messageRequestsEnabled: true, - }); - - return ; -} - -export function SmsOnlyFetchingUuid(): JSX.Element { - const props = useProps({ - isSMSOnly: true, - isFetchingUUID: true, - }); - - return ; -} - -SmsOnlyFetchingUuid.story = { - name: 'SMS-only fetching UUID', -}; - -export function SmsOnly(): JSX.Element { - const props = useProps({ - isSMSOnly: true, - }); - - return ; -} - -SmsOnly.story = { - name: 'SMS-only', -}; - -export function Attachments(): JSX.Element { - const props = useProps({ - draftAttachments: [ - fakeDraftAttachment({ - contentType: IMAGE_JPEG, - url: landscapeGreenUrl, - }), - ], - }); - - return ; -} - -export function PendingApproval(): JSX.Element { +export function StartingText(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); return ( ); } -AnnouncementsOnlyGroup.story = { - name: 'Announcements Only group', -}; - -export function AnnouncementsOnlyGroup(): JSX.Element { +export function StickerButton(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); return ( ); } -AnnouncementsOnlyGroup.story = { - name: 'Announcements Only group', -}; +export function MessageRequest(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ; +} -export function Quote(): JSX.Element { +export function SmsOnlyFetchingUuid(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ; +} + +export function SmsOnly(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ; +} + +export function Attachments(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); return ( + ); +} + +export function PendingApproval(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ; +} + +export function AnnouncementsOnlyGroup(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ( + + ); +} + +export function Quote(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ( + + ); +} + +export function QuoteWithPayment(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ( + ); } -export function QuoteWithPayment(): JSX.Element { +export function NoFormattingMenu(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ( + + ); +} + +export function NoFormattingFlag(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); + return ( + + ); +} + +export function NoSpoilerFormattingFlag(args: Props): JSX.Element { + const theme = useContext(StorybookThemeContext); return ( - ); -} - -QuoteWithPayment.story = { - name: 'Quote with payment', -}; - -export function NoFormattingMenu(): JSX.Element { - return ; -} - -export function NoFormattingFlag(): JSX.Element { - return ; -} - -export function NoSpoilerFormattingFlag(): JSX.Element { - return ( - ); } diff --git a/ts/components/CompositionInput.stories.tsx b/ts/components/CompositionInput.stories.tsx index e9b57d1dc097..86371085bdc5 100644 --- a/ts/components/CompositionInput.stories.tsx +++ b/ts/components/CompositionInput.stories.tsx @@ -2,11 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import 'react-quill/dist/quill.core.css'; -import { boolean, select } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import type { Props } from './CompositionInput'; import { CompositionInput } from './CompositionInput'; @@ -19,11 +17,13 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/CompositionInput', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const useProps = (overrideProps: Partial = {}): Props => ({ i18n, - disabled: boolean('disabled', overrideProps.disabled || false), + disabled: overrideProps.disabled ?? false, draftText: overrideProps.draftText || undefined, draftBodyRanges: overrideProps.draftBodyRanges || [], clearQuotedMessage: action('clearQuotedMessage'), @@ -41,7 +41,7 @@ const useProps = (overrideProps: Partial = {}): Props => ({ overrideProps.isFormattingEnabled === false ? overrideProps.isFormattingEnabled : true, - large: boolean('large', overrideProps.large || false), + large: overrideProps.large ?? false, onCloseLinkPreview: action('onCloseLinkPreview'), onEditorStateChange: action('onEditorStateChange'), onPickEmoji: action('onPickEmoji'), @@ -49,19 +49,8 @@ const useProps = (overrideProps: Partial = {}): Props => ({ onTextTooLong: action('onTextTooLong'), platform: 'darwin', sendCounter: 0, - sortedGroupMembers: overrideProps.sortedGroupMembers || [], - skinTone: select( - 'skinTone', - { - skinTone0: 0, - skinTone1: 1, - skinTone2: 2, - skinTone3: 3, - skinTone4: 4, - skinTone5: 5, - }, - overrideProps.skinTone || undefined - ), + sortedGroupMembers: overrideProps.sortedGroupMembers ?? [], + skinTone: overrideProps.skinTone ?? undefined, theme: React.useContext(StorybookThemeContext), }); diff --git a/ts/components/CompositionRecording.stories.tsx b/ts/components/CompositionRecording.stories.tsx index d537c39447fd..7619ccb93798 100644 --- a/ts/components/CompositionRecording.stories.tsx +++ b/ts/components/CompositionRecording.stories.tsx @@ -3,8 +3,9 @@ import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { Props } from './CompositionRecording'; import { CompositionRecording } from './CompositionRecording'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -13,7 +14,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'components/CompositionRecording', component: CompositionRecording, -}; +} satisfies Meta; export function Default(): JSX.Element { const [active, setActive] = useState(false); diff --git a/ts/components/CompositionRecordingDraft.stories.tsx b/ts/components/CompositionRecordingDraft.stories.tsx index 10d966b5a67e..ac59696708be 100644 --- a/ts/components/CompositionRecordingDraft.stories.tsx +++ b/ts/components/CompositionRecordingDraft.stories.tsx @@ -3,8 +3,9 @@ import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { Props } from './CompositionRecordingDraft'; import { CompositionRecordingDraft } from './CompositionRecordingDraft'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -13,7 +14,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'components/CompositionRecordingDraft', component: CompositionRecordingDraft, -}; +} satisfies Meta; export function Default(): JSX.Element { const [isPlaying, setIsPlaying] = useState(false); diff --git a/ts/components/CompositionRecordingDraft.tsx b/ts/components/CompositionRecordingDraft.tsx index ae26b10502d7..de09f672ecfc 100644 --- a/ts/components/CompositionRecordingDraft.tsx +++ b/ts/components/CompositionRecordingDraft.tsx @@ -11,7 +11,7 @@ import * as log from '../logging/log'; import type { Size } from '../hooks/useSizeObserver'; import { SizeObserver } from '../hooks/useSizeObserver'; -type Props = { +export type Props = { i18n: LocalizerType; audioUrl: string | undefined; active: diff --git a/ts/components/ConfirmDiscardDialog.stories.tsx b/ts/components/ConfirmDiscardDialog.stories.tsx index 9f3bb14976fa..04ef5da27572 100644 --- a/ts/components/ConfirmDiscardDialog.stories.tsx +++ b/ts/components/ConfirmDiscardDialog.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -20,7 +21,7 @@ const createProps = (): PropsType => ({ export default { title: 'Components/ConfirmDiscardDialog', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/ConfirmationDialog.stories.tsx b/ts/components/ConfirmationDialog.stories.tsx index 7a3aec6b5a19..629e19e1f443 100644 --- a/ts/components/ConfirmationDialog.stories.tsx +++ b/ts/components/ConfirmationDialog.stories.tsx @@ -4,6 +4,8 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { Props } from './ConfirmationDialog'; import { ConfirmationDialog } from './ConfirmationDialog'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -12,9 +14,9 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/ConfirmationDialog', -}; +} satisfies Meta; -export const _ConfirmationDialog = (): JSX.Element => { +export function Basic(): JSX.Element { return ( { asdf blip ); -}; - -_ConfirmationDialog.story = { - name: 'ConfirmationDialog', -}; +} export function CustomCancelText(): JSX.Element { return ( @@ -64,10 +62,6 @@ export function CustomCancelText(): JSX.Element { ); } -CustomCancelText.story = { - name: 'Custom cancel text', -}; - export function NoDefaultCancel(): JSX.Element { return ( ; type ContactType = Omit; @@ -54,10 +55,6 @@ export function EmptyList(): JSX.Element { return ; } -EmptyList.story = { - name: 'Empty list', -}; - export function OneContact(): JSX.Element { return ( @@ -66,10 +63,6 @@ export function OneContact(): JSX.Element { ); } -OneContact.story = { - name: 'One contact', -}; - export function ThreeContacts(): JSX.Element { return ( @@ -80,10 +73,6 @@ export function ThreeContacts(): JSX.Element { ); } -ThreeContacts.story = { - name: 'Three contacts', -}; - export function FourContactsOneWithALongName(): JSX.Element { return ( @@ -101,10 +90,6 @@ export function FourContactsOneWithALongName(): JSX.Element { ); } -FourContactsOneWithALongName.story = { - name: 'Four contacts, one with a long name', -}; - export function FiftyContacts(): JSX.Element { return ( @@ -114,7 +99,3 @@ export function FiftyContacts(): JSX.Element { ); } - -FiftyContacts.story = { - name: 'Fifty contacts', -}; diff --git a/ts/components/ContextMenu.stories.tsx b/ts/components/ContextMenu.stories.tsx index 5dc2b038311b..cdb4e70f3396 100644 --- a/ts/components/ContextMenu.stories.tsx +++ b/ts/components/ContextMenu.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './ContextMenu'; import { ContextMenu } from './ContextMenu'; import enMessages from '../../_locales/en/messages.json'; @@ -13,7 +14,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/ContextMenu', -}; +} satisfies Meta>; const getDefaultProps = (): PropsType => ({ i18n, diff --git a/ts/components/ConversationList.stories.tsx b/ts/components/ConversationList.stories.tsx index 8e2db3fe4333..7422077a7157 100644 --- a/ts/components/ConversationList.stories.tsx +++ b/ts/components/ConversationList.stories.tsx @@ -4,11 +4,9 @@ import React, { useContext } from 'react'; import { times, omit } from 'lodash'; import { v4 as generateUuid } from 'uuid'; - import { action } from '@storybook/addon-actions'; -import { boolean, date, select, text } from '@storybook/addon-knobs'; - -import type { Row } from './ConversationList'; +import type { Meta } from '@storybook/react'; +import type { Row, PropsType } from './ConversationList'; import { ConversationList, RowType } from './ConversationList'; import { MessageSearchResult } from './conversationList/MessageSearchResult'; import type { PropsData as ConversationListItemPropsType } from './conversationList/ConversationListItem'; @@ -25,7 +23,9 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/ConversationList', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const defaultConversations: Array = [ getDefaultConversation({ @@ -105,15 +105,13 @@ function Wrapper({ ); } -export const _ArchiveButton = (): JSX.Element => ( - -); - -_ArchiveButton.story = { - name: 'Archive button', -}; +export function ArchiveButton(): JSX.Element { + return ( + + ); +} export function ContactNoteToSelf(): JSX.Element { return ( @@ -132,10 +130,6 @@ export function ContactNoteToSelf(): JSX.Element { ); } -ContactNoteToSelf.story = { - name: 'Contact: note to self', -}; - export function ContactDirect(): JSX.Element { return ( = {} ): ConversationListItemPropsType => ({ ...overrideProps, - acceptedMessageRequest: boolean( - 'acceptedMessageRequest', + acceptedMessageRequest: overrideProps.acceptedMessageRequest !== undefined ? overrideProps.acceptedMessageRequest - : true - ), + : true, badges: [], - isMe: boolean('isMe', overrideProps.isMe || false), - avatarPath: text('avatarPath', overrideProps.avatarPath || ''), + isMe: overrideProps.isMe ?? false, + avatarPath: overrideProps.avatarPath ?? '', id: overrideProps.id || '', - isSelected: boolean('isSelected', overrideProps.isSelected || false), - title: text('title', overrideProps.title || 'Some Person'), + isSelected: overrideProps.isSelected ?? false, + title: overrideProps.title ?? 'Some Person', profileName: overrideProps.profileName || 'Some Person', type: overrideProps.type || 'direct', - markedUnread: boolean('markedUnread', overrideProps.markedUnread || false), + markedUnread: overrideProps.markedUnread ?? false, lastMessage: overrideProps.lastMessage || { - text: text('lastMessage.text', 'Hi there!'), - status: select( - 'status', - MessageStatuses.reduce((m, s) => ({ ...m, [s]: s }), {}), - 'read' - ), + text: 'Hi there!', + status: 'read', deletedForEveryone: false, }, - lastUpdated: date( - 'lastUpdated', - new Date(overrideProps.lastUpdated || Date.now() - 5 * 60 * 1000) - ), + lastUpdated: overrideProps.lastUpdated ?? Date.now() - 5 * 60 * 1000, sharedGroupNames: [], }); @@ -333,19 +290,11 @@ const renderConversation = ( export const ConversationName = (): JSX.Element => renderConversation(); -ConversationName.story = { - name: 'Conversation: name', -}; - export const ConversationNameAndAvatar = (): JSX.Element => renderConversation({ avatarPath: '/fixtures/kitten-1-64-64.jpg', }); -ConversationNameAndAvatar.story = { - name: 'Conversation: name and avatar', -}; - export const ConversationWithYourself = (): JSX.Element => renderConversation({ lastMessage: { @@ -358,10 +307,6 @@ export const ConversationWithYourself = (): JSX.Element => isMe: true, }); -ConversationWithYourself.story = { - name: 'Conversation: with yourself', -}; - export function ConversationsMessageStatuses(): JSX.Element { return ( renderConversation({ typingContactIdTimestamps: { - [generateUuid()]: date('timestamp', new Date()), + [generateUuid()]: Date.now(), }, }); -ConversationTypingStatus.story = { - name: 'Conversation: Typing Status', -}; - export const ConversationWithDraft = (): JSX.Element => renderConversation({ shouldShowDraft: true, @@ -400,19 +337,11 @@ export const ConversationWithDraft = (): JSX.Element => }, }); -ConversationWithDraft.story = { - name: 'Conversation: With draft', -}; - export const ConversationDeletedForEveryone = (): JSX.Element => renderConversation({ lastMessage: { deletedForEveryone: true }, }); -ConversationDeletedForEveryone.story = { - name: 'Conversation: Deleted for everyone', -}; - export const ConversationMessageRequest = (): JSX.Element => renderConversation({ acceptedMessageRequest: false, @@ -423,10 +352,6 @@ export const ConversationMessageRequest = (): JSX.Element => }, }); -ConversationMessageRequest.story = { - name: 'Conversation: Message Request', -}; - export function ConversationsUnreadCount(): JSX.Element { return ( renderConversation({ markedUnread: true }); -ConversationMarkedUnread.story = { - name: 'Conversation: marked unread', -}; - export const ConversationSelected = (): JSX.Element => renderConversation({ lastMessage: { @@ -466,10 +383,6 @@ export const ConversationSelected = (): JSX.Element => isSelected: true, }); -ConversationSelected.story = { - name: 'Conversation: Selected', -}; - export const ConversationEmojiInMessage = (): JSX.Element => renderConversation({ lastMessage: { @@ -479,10 +392,6 @@ export const ConversationEmojiInMessage = (): JSX.Element => }, }); -ConversationEmojiInMessage.story = { - name: 'Conversation: Emoji in Message', -}; - export const ConversationLinkInMessage = (): JSX.Element => renderConversation({ lastMessage: { @@ -492,10 +401,6 @@ export const ConversationLinkInMessage = (): JSX.Element => }, }); -ConversationLinkInMessage.story = { - name: 'Conversation: Link in Message', -}; - export const ConversationLongName = (): JSX.Element => { const name = 'Long contact name. Esquire. The third. And stuff. And more! And more!'; @@ -505,10 +410,6 @@ export const ConversationLongName = (): JSX.Element => { }); }; -ConversationLongName.story = { - name: 'Conversation: long name', -}; - export function ConversationLongMessage(): JSX.Element { const messages = [ "Long line. This is a really really really long line. Really really long. Because that's just how it is", @@ -534,10 +435,6 @@ Line 4, well.`, ); } -ConversationLongMessage.story = { - name: 'Conversation: Long Message', -}; - export function ConversationsVariousTimes(): JSX.Element { const pairs: Array<[number, string]> = [ [Date.now() - 5 * 60 * 60 * 1000, 'Five hours ago'], @@ -563,10 +460,6 @@ export function ConversationsVariousTimes(): JSX.Element { ); } -ConversationsVariousTimes.story = { - name: 'Conversations: Various Times', -}; - export function ConversationMissingDate(): JSX.Element { const row = { type: RowType.Conversation as const, @@ -576,10 +469,6 @@ export function ConversationMissingDate(): JSX.Element { return ; } -ConversationMissingDate.story = { - name: 'Conversation: Missing Date', -}; - export function ConversationMissingMessage(): JSX.Element { const row = { type: RowType.Conversation as const, @@ -589,10 +478,6 @@ export function ConversationMissingMessage(): JSX.Element { return ; } -ConversationMissingMessage.story = { - name: 'Conversation: Missing Message', -}; - export const ConversationMissingText = (): JSX.Element => renderConversation({ lastMessage: { @@ -602,19 +487,11 @@ export const ConversationMissingText = (): JSX.Element => }, }); -ConversationMissingText.story = { - name: 'Conversation: Missing Text', -}; - export const ConversationMutedConversation = (): JSX.Element => renderConversation({ muteExpiresAt: Date.now() + 1000 * 60 * 60, }); -ConversationMutedConversation.story = { - name: 'Conversation: Muted Conversation', -}; - export const ConversationAtMention = (): JSX.Element => renderConversation({ title: 'The Rebellion', @@ -626,10 +503,6 @@ export const ConversationAtMention = (): JSX.Element => }, }); -ConversationAtMention.story = { - name: 'Conversation: At Mention', -}; - export function Headers(): JSX.Element { return ( ); } - -KitchenSink.story = { - name: 'Kitchen sink', -}; diff --git a/ts/components/CrashReportDialog.stories.tsx b/ts/components/CrashReportDialog.stories.tsx index 3c5c942b3d31..dc45a2dde1e0 100644 --- a/ts/components/CrashReportDialog.stories.tsx +++ b/ts/components/CrashReportDialog.stories.tsx @@ -4,6 +4,8 @@ import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './CrashReportDialog'; import { CrashReportDialog } from './CrashReportDialog'; import { setupI18n } from '../util/setupI18n'; import { sleep } from '../util/sleep'; @@ -11,11 +13,11 @@ import enMessages from '../../_locales/en/messages.json'; export default { title: 'Components/CrashReportDialog', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); -export const _CrashReportDialog = (): JSX.Element => { +export function Basic(): JSX.Element { const [isPending, setIsPending] = useState(false); return ( @@ -31,8 +33,4 @@ export const _CrashReportDialog = (): JSX.Element => { eraseCrashReports={action('eraseCrashReports')} /> ); -}; - -_CrashReportDialog.story = { - name: 'CrashReportDialog', -}; +} diff --git a/ts/components/CrashReportDialog.tsx b/ts/components/CrashReportDialog.tsx index 28e73d56e2e3..4f20b62c3c40 100644 --- a/ts/components/CrashReportDialog.tsx +++ b/ts/components/CrashReportDialog.tsx @@ -13,7 +13,7 @@ type PropsActionsType = { eraseCrashReports: () => void; }; -type PropsType = { +export type PropsType = { i18n: LocalizerType; isPending: boolean; } & PropsActionsType; diff --git a/ts/components/CustomColorEditor.stories.tsx b/ts/components/CustomColorEditor.stories.tsx index 173e7cc454d6..91beaaee45ca 100644 --- a/ts/components/CustomColorEditor.stories.tsx +++ b/ts/components/CustomColorEditor.stories.tsx @@ -5,6 +5,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import type { PropsType } from './CustomColorEditor'; import { CustomColorEditor } from './CustomColorEditor'; @@ -12,7 +13,7 @@ import { setupI18n } from '../util/setupI18n'; export default { title: 'Components/CustomColorEditor', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); diff --git a/ts/components/CustomizingPreferredReactionsModal.stories.tsx b/ts/components/CustomizingPreferredReactionsModal.stories.tsx index 62a69bc67771..443b430bd14b 100644 --- a/ts/components/CustomizingPreferredReactionsModal.stories.tsx +++ b/ts/components/CustomizingPreferredReactionsModal.stories.tsx @@ -5,16 +5,18 @@ import type { ComponentProps } from 'react'; import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; +import type { PropsType } from './CustomizingPreferredReactionsModal'; import { CustomizingPreferredReactionsModal } from './CustomizingPreferredReactionsModal'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/CustomizingPreferredReactionsModal', -}; +} satisfies Meta; const defaultProps: ComponentProps = { @@ -50,10 +52,6 @@ export function DraftEmojiSelected(): JSX.Element { ); } -DraftEmojiSelected.story = { - name: 'Draft emoji selected', -}; - export function Saving(): JSX.Element { return ; } @@ -61,7 +59,3 @@ export function Saving(): JSX.Element { export function HadError(): JSX.Element { return ; } - -HadError.story = { - name: 'Had error', -}; diff --git a/ts/components/CustomizingPreferredReactionsModal.tsx b/ts/components/CustomizingPreferredReactionsModal.tsx index ae50f9511b46..073cc06ac61a 100644 --- a/ts/components/CustomizingPreferredReactionsModal.tsx +++ b/ts/components/CustomizingPreferredReactionsModal.tsx @@ -19,7 +19,7 @@ import { convertShortName } from './emoji/lib'; import { offsetDistanceModifier } from '../util/popperUtil'; import { handleOutsideClick } from '../util/handleOutsideClick'; -type PropsType = { +export type PropsType = { draftPreferredReactions: ReadonlyArray; hadSaveError: boolean; i18n: LocalizerType; diff --git a/ts/components/DebugLogWindow.stories.tsx b/ts/components/DebugLogWindow.stories.tsx index 7118a5bbe81c..2e226a0db0ea 100644 --- a/ts/components/DebugLogWindow.stories.tsx +++ b/ts/components/DebugLogWindow.stories.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import type { PropsType } from './DebugLogWindow'; import { DebugLogWindow } from './DebugLogWindow'; @@ -31,12 +32,8 @@ const createProps = (): PropsType => ({ export default { title: 'Components/DebugLogWindow', -}; +} satisfies Meta; -export const _DebugLogWindow = (): JSX.Element => ( - -); - -_DebugLogWindow.story = { - name: 'DebugLogWindow', -}; +export function Basic(): JSX.Element { + return ; +} diff --git a/ts/components/DialogExpiredBuild.stories.tsx b/ts/components/DialogExpiredBuild.stories.tsx index 11df133f0a31..2d8b732e6333 100644 --- a/ts/components/DialogExpiredBuild.stories.tsx +++ b/ts/components/DialogExpiredBuild.stories.tsx @@ -2,8 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { select } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './DialogExpiredBuild'; import { DialogExpiredBuild } from './DialogExpiredBuild'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -14,14 +14,12 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/DialogExpiredBuild', -}; + argTypes: {}, + args: {}, +} satisfies Meta; -export const _DialogExpiredBuild = (): JSX.Element => { - const containerWidthBreakpoint = select( - 'containerWidthBreakpoint', - WidthBreakpoint, - WidthBreakpoint.Wide - ); +export function Basic(): JSX.Element { + const containerWidthBreakpoint = WidthBreakpoint.Wide; return ( @@ -31,8 +29,4 @@ export const _DialogExpiredBuild = (): JSX.Element => { /> ); -}; - -_DialogExpiredBuild.story = { - name: 'DialogExpiredBuild', -}; +} diff --git a/ts/components/DialogNetworkStatus.stories.tsx b/ts/components/DialogNetworkStatus.stories.tsx index 9bcf717e1ac0..cfbec3cc14cb 100644 --- a/ts/components/DialogNetworkStatus.stories.tsx +++ b/ts/components/DialogNetworkStatus.stories.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './DialogNetworkStatus'; import { DialogNetworkStatus } from './DialogNetworkStatus'; import { SocketStatus } from '../types/SocketStatus'; @@ -27,7 +28,7 @@ const defaultProps = { export default { title: 'Components/DialogNetworkStatus', -}; +} satisfies Meta; export function KnobsPlayground(args: PropsType): JSX.Element { /* @@ -68,10 +69,6 @@ export function ConnectingWide(): JSX.Element { ); } -ConnectingWide.story = { - name: 'Connecting Wide', -}; - export function ClosingWide(): JSX.Element { return ( @@ -84,10 +81,6 @@ export function ClosingWide(): JSX.Element { ); } -ClosingWide.story = { - name: 'Closing Wide', -}; - export function ClosedWide(): JSX.Element { return ( @@ -100,10 +93,6 @@ export function ClosedWide(): JSX.Element { ); } -ClosedWide.story = { - name: 'Closed Wide', -}; - export function OfflineWide(): JSX.Element { return ( @@ -116,10 +105,6 @@ export function OfflineWide(): JSX.Element { ); } -OfflineWide.story = { - name: 'Offline Wide', -}; - export function ConnectingNarrow(): JSX.Element { return ( @@ -132,10 +117,6 @@ export function ConnectingNarrow(): JSX.Element { ); } -ConnectingNarrow.story = { - name: 'Connecting Narrow', -}; - export function ClosingNarrow(): JSX.Element { return ( @@ -148,10 +129,6 @@ export function ClosingNarrow(): JSX.Element { ); } -ClosingNarrow.story = { - name: 'Closing Narrow', -}; - export function ClosedNarrow(): JSX.Element { return ( @@ -164,10 +141,6 @@ export function ClosedNarrow(): JSX.Element { ); } -ClosedNarrow.story = { - name: 'Closed Narrow', -}; - export function OfflineNarrow(): JSX.Element { return ( @@ -179,7 +152,3 @@ export function OfflineNarrow(): JSX.Element { ); } - -OfflineNarrow.story = { - name: 'Offline Narrow', -}; diff --git a/ts/components/DialogRelink.stories.tsx b/ts/components/DialogRelink.stories.tsx index 813da6e4561c..01ab3e7fcbcd 100644 --- a/ts/components/DialogRelink.stories.tsx +++ b/ts/components/DialogRelink.stories.tsx @@ -3,7 +3,8 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './DialogRelink'; import { DialogRelink } from './DialogRelink'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -35,7 +36,7 @@ const permutations = [ export default { title: 'Components/DialogRelink', -}; +} satisfies Meta; export function Iterations(): JSX.Element { return ( diff --git a/ts/components/DialogUpdate.stories.tsx b/ts/components/DialogUpdate.stories.tsx index 4e202cd5f9b3..097206d81259 100644 --- a/ts/components/DialogUpdate.stories.tsx +++ b/ts/components/DialogUpdate.stories.tsx @@ -2,8 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { select } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './DialogUpdate'; import { DialogUpdate } from './DialogUpdate'; import { DialogType } from '../types/Dialogs'; import { WidthBreakpoint } from './_util'; @@ -29,15 +30,13 @@ const defaultProps = { export default { title: 'Components/DialogUpdate', -}; + argTypes: {}, + args: {}, +} satisfies Meta; export function KnobsPlayground(): JSX.Element { - const containerWidthBreakpoint = select( - 'containerWidthBreakpoint', - WidthBreakpoint, - WidthBreakpoint.Wide - ); - const dialogType = select('dialogType', DialogType, DialogType.AutoUpdate); + const containerWidthBreakpoint = WidthBreakpoint.Wide; + const dialogType = DialogType.AutoUpdate; return ( @@ -64,10 +63,6 @@ export function UpdateWide(): JSX.Element { ); } -UpdateWide.story = { - name: 'Update (Wide)', -}; - export function DownloadedWide(): JSX.Element { return ( @@ -81,10 +76,6 @@ export function DownloadedWide(): JSX.Element { ); } -DownloadedWide.story = { - name: 'Downloaded (Wide)', -}; - export function DownloadReadyWide(): JSX.Element { return ( @@ -99,10 +90,6 @@ export function DownloadReadyWide(): JSX.Element { ); } -DownloadReadyWide.story = { - name: 'DownloadReady (Wide)', -}; - export function FullDownloadReadyWide(): JSX.Element { return ( @@ -117,10 +104,6 @@ export function FullDownloadReadyWide(): JSX.Element { ); } -FullDownloadReadyWide.story = { - name: 'FullDownloadReady (Wide)', -}; - export function DownloadingWide(): JSX.Element { const [downloadedSize, setDownloadedSize] = React.useState(0); @@ -153,10 +136,6 @@ export function DownloadingWide(): JSX.Element { ); } -DownloadingWide.story = { - name: 'Downloading (Wide)', -}; - export function CannotUpdateWide(): JSX.Element { return ( @@ -170,10 +149,6 @@ export function CannotUpdateWide(): JSX.Element { ); } -CannotUpdateWide.story = { - name: 'Cannot_Update (Wide)', -}; - export function CannotUpdateBetaWide(): JSX.Element { return ( @@ -187,10 +162,6 @@ export function CannotUpdateBetaWide(): JSX.Element { ); } -CannotUpdateBetaWide.story = { - name: 'Cannot_Update_Beta (Wide)', -}; - export function CannotUpdateRequireManualWide(): JSX.Element { return ( @@ -204,10 +175,6 @@ export function CannotUpdateRequireManualWide(): JSX.Element { ); } -CannotUpdateRequireManualWide.story = { - name: 'Cannot_Update_Require_Manual (Wide)', -}; - export function CannotUpdateRequireManualBetaWide(): JSX.Element { return ( @@ -221,10 +188,6 @@ export function CannotUpdateRequireManualBetaWide(): JSX.Element { ); } -CannotUpdateRequireManualBetaWide.story = { - name: 'Cannot_Update_Require_Manual_Beta (Wide)', -}; - export function MacOSReadOnlyWide(): JSX.Element { return ( @@ -238,10 +201,6 @@ export function MacOSReadOnlyWide(): JSX.Element { ); } -MacOSReadOnlyWide.story = { - name: 'MacOS_Read_Only (Wide)', -}; - export function UnsupportedOSWide(): JSX.Element { return ( @@ -255,10 +214,6 @@ export function UnsupportedOSWide(): JSX.Element { ); } -UnsupportedOSWide.story = { - name: 'UnsupportedOS (Wide)', -}; - export function UpdateNarrow(): JSX.Element { return ( @@ -272,10 +227,6 @@ export function UpdateNarrow(): JSX.Element { ); } -UpdateNarrow.story = { - name: 'Update (Narrow)', -}; - export function DownloadedNarrow(): JSX.Element { return ( @@ -289,10 +240,6 @@ export function DownloadedNarrow(): JSX.Element { ); } -DownloadedNarrow.story = { - name: 'Downloaded (Narrow)', -}; - export function DownloadReadyNarrow(): JSX.Element { return ( @@ -307,10 +254,6 @@ export function DownloadReadyNarrow(): JSX.Element { ); } -DownloadReadyNarrow.story = { - name: 'DownloadReady (Narrow)', -}; - export function FullDownloadReadyNarrow(): JSX.Element { return ( @@ -325,10 +268,6 @@ export function FullDownloadReadyNarrow(): JSX.Element { ); } -FullDownloadReadyNarrow.story = { - name: 'FullDownloadReady (Narrow)', -}; - export function DownloadingNarrow(): JSX.Element { return ( @@ -342,10 +281,6 @@ export function DownloadingNarrow(): JSX.Element { ); } -DownloadingNarrow.story = { - name: 'Downloading (Narrow)', -}; - export function CannotUpdateNarrow(): JSX.Element { return ( @@ -359,10 +294,6 @@ export function CannotUpdateNarrow(): JSX.Element { ); } -CannotUpdateNarrow.story = { - name: 'Cannot Update (Narrow)', -}; - export function CannotUpdateBetaNarrow(): JSX.Element { return ( @@ -376,10 +307,6 @@ export function CannotUpdateBetaNarrow(): JSX.Element { ); } -CannotUpdateBetaNarrow.story = { - name: 'Cannot Update Beta (Narrow)', -}; - export function CannotUpdateRequireManualNarrow(): JSX.Element { return ( @@ -393,10 +320,6 @@ export function CannotUpdateRequireManualNarrow(): JSX.Element { ); } -CannotUpdateRequireManualNarrow.story = { - name: 'Cannot_Update_Require_Manual (Narrow)', -}; - export function CannotUpdateRequireManualBetaNarrow(): JSX.Element { return ( @@ -410,10 +333,6 @@ export function CannotUpdateRequireManualBetaNarrow(): JSX.Element { ); } -CannotUpdateRequireManualBetaNarrow.story = { - name: 'Cannot_Update_Require_Manual_Beta (Narrow)', -}; - export function MacOSReadOnlyNarrow(): JSX.Element { return ( @@ -427,10 +346,6 @@ export function MacOSReadOnlyNarrow(): JSX.Element { ); } -MacOSReadOnlyNarrow.story = { - name: 'MacOS_Read_Only (Narrow)', -}; - export function UnsupportedOSNarrow(): JSX.Element { return ( @@ -443,7 +358,3 @@ export function UnsupportedOSNarrow(): JSX.Element { ); } - -UnsupportedOSNarrow.story = { - name: 'UnsupportedOS (Narrow)', -}; diff --git a/ts/components/DisappearingTimeDialog.stories.tsx b/ts/components/DisappearingTimeDialog.stories.tsx index 3ed55aa89adf..69a99e115716 100644 --- a/ts/components/DisappearingTimeDialog.stories.tsx +++ b/ts/components/DisappearingTimeDialog.stories.tsx @@ -3,7 +3,8 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './DisappearingTimeDialog'; import { DisappearingTimeDialog } from './DisappearingTimeDialog'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -12,7 +13,7 @@ import { EXPIRE_TIMERS } from '../test-both/util/expireTimers'; export default { title: 'Components/DisappearingTimeDialog', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); diff --git a/ts/components/DisappearingTimerSelect.stories.tsx b/ts/components/DisappearingTimerSelect.stories.tsx index 11d8973f0cd4..460abf1d6f03 100644 --- a/ts/components/DisappearingTimerSelect.stories.tsx +++ b/ts/components/DisappearingTimerSelect.stories.tsx @@ -2,7 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState } from 'react'; - +import type { Meta } from '@storybook/react'; +import type { Props } from './DisappearingTimerSelect'; import { DisappearingTimerSelect } from './DisappearingTimerSelect'; import { setupI18n } from '../util/setupI18n'; import { DurationInSeconds } from '../util/durations'; @@ -10,15 +11,15 @@ import enMessages from '../../_locales/en/messages.json'; export default { title: 'Components/DisappearingTimerSelect', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); -type Props = { +type Args = { initialValue: number; }; -function TimerSelectWrap({ initialValue }: Props): JSX.Element { +function TimerSelectWrap({ initialValue }: Args): JSX.Element { const [value, setValue] = useState(initialValue); return ( @@ -34,14 +35,6 @@ export function InitialValue1Day(): JSX.Element { return ; } -InitialValue1Day.story = { - name: 'Initial value: 1 day', -}; - export function InitialValue3DaysCustomTime(): JSX.Element { return ; } - -InitialValue3DaysCustomTime.story = { - name: 'Initial value 3 days (Custom time)', -}; diff --git a/ts/components/EditUsernameModalBody.stories.tsx b/ts/components/EditUsernameModalBody.stories.tsx index 1e9fed1539a9..92a9db4885b6 100644 --- a/ts/components/EditUsernameModalBody.stories.tsx +++ b/ts/components/EditUsernameModalBody.stories.tsx @@ -2,8 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; import enMessages from '../../_locales/en/messages.json'; import { setupI18n } from '../util/setupI18n'; import type { UsernameReservationType } from '../types/Username'; @@ -29,11 +30,9 @@ export default { argTypes: { currentUsername: { type: { name: 'string', required: false }, - defaultValue: undefined, }, state: { control: { type: 'radio' }, - defaultValue: State.Open, options: { Open: State.Open, Closed: State.Closed, @@ -43,7 +42,6 @@ export default { }, error: { control: { type: 'radio' }, - defaultValue: undefined, options: { None: undefined, NotEnoughCharacters: UsernameReservationError.NotEnoughCharacters, @@ -54,26 +52,24 @@ export default { General: UsernameReservationError.General, }, }, - maxUsername: { - defaultValue: 20, - }, - minUsername: { - defaultValue: 3, - }, - discriminator: { + reservation: { type: { name: 'string', required: false }, - defaultValue: undefined, }, - i18n: { - defaultValue: i18n, - }, - onClose: { action: true }, - onError: { action: true }, - setUsernameReservationError: { action: true }, - reserveUsername: { action: true }, - confirmUsername: { action: true }, }, -} as Meta; + args: { + currentUsername: undefined, + state: State.Open, + error: undefined, + maxNickname: 20, + minNickname: 3, + reservation: undefined, + i18n, + onClose: action('onClose'), + setUsernameReservationError: action('setUsernameReservationError'), + reserveUsername: action('reserveUsername'), + confirmUsername: action('confirmUsername'), + }, +} satisfies Meta; type ArgsType = PropsType & { discriminator?: string; @@ -81,7 +77,7 @@ type ArgsType = PropsType & { }; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => { +const Template: StoryFn = args => { let { reservation } = args; if (!reservation && args.discriminator) { reservation = { @@ -95,27 +91,16 @@ const Template: Story = args => { export const WithoutUsername = Template.bind({}); WithoutUsername.args = {}; -WithoutUsername.story = { - name: 'without current username', -}; export const WithUsername = Template.bind({}); -WithUsername.args = {}; -WithUsername.story = { - name: 'with current username', - args: { - currentUsername: 'signaluser.12', - }, +WithUsername.args = { + currentUsername: 'signaluser.12', }; export const WithReservation = Template.bind({}); -WithReservation.args = {}; -WithReservation.story = { - name: 'with reservation', - args: { - currentUsername: 'reserved', - reservation: DEFAULT_RESERVATION, - }, +WithReservation.args = { + currentUsername: 'reserved', + reservation: DEFAULT_RESERVATION, }; export const UsernameEditingConfirming = Template.bind({}); @@ -123,9 +108,6 @@ UsernameEditingConfirming.args = { state: State.Confirming, currentUsername: 'signaluser.12', }; -UsernameEditingConfirming.story = { - name: 'Username editing, Confirming', -}; export const UsernameEditingUsernameTaken = Template.bind({}); UsernameEditingUsernameTaken.args = { @@ -133,9 +115,6 @@ UsernameEditingUsernameTaken.args = { error: UsernameReservationError.UsernameNotAvailable, currentUsername: 'signaluser.12', }; -UsernameEditingUsernameTaken.story = { - name: 'Username editing, username taken', -}; export const UsernameEditingUsernameWrongCharacters = Template.bind({}); UsernameEditingUsernameWrongCharacters.args = { @@ -143,9 +122,6 @@ UsernameEditingUsernameWrongCharacters.args = { error: UsernameReservationError.CheckCharacters, currentUsername: 'signaluser.12', }; -UsernameEditingUsernameWrongCharacters.story = { - name: 'Username editing, Wrong Characters', -}; export const UsernameEditingUsernameTooShort = Template.bind({}); UsernameEditingUsernameTooShort.args = { @@ -153,9 +129,6 @@ UsernameEditingUsernameTooShort.args = { error: UsernameReservationError.NotEnoughCharacters, currentUsername: 'sig', }; -UsernameEditingUsernameTooShort.story = { - name: 'Username editing, username too short', -}; export const UsernameEditingGeneralError = Template.bind({}); UsernameEditingGeneralError.args = { @@ -163,6 +136,3 @@ UsernameEditingGeneralError.args = { error: UsernameReservationError.General, currentUsername: 'signaluser.12', }; -UsernameEditingGeneralError.story = { - name: 'Username editing, general error', -}; diff --git a/ts/components/ErrorModal.stories.tsx b/ts/components/ErrorModal.stories.tsx index 51a3d40b7a95..20775c3187c4 100644 --- a/ts/components/ErrorModal.stories.tsx +++ b/ts/components/ErrorModal.stories.tsx @@ -2,9 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './ErrorModal'; import { ErrorModal } from './ErrorModal'; @@ -14,15 +14,17 @@ import enMessages from '../../_locales/en/messages.json'; const i18n = setupI18n('en', enMessages); const createProps = (overrideProps: Partial = {}): PropsType => ({ - title: text('title', overrideProps.title || ''), - description: text('description', overrideProps.description || ''), + title: overrideProps.title ?? '', + description: overrideProps.description ?? '', i18n, onClose: action('onClick'), }); export default { title: 'Components/ErrorModal', -}; + argTypes: {}, + args: {}, +} satisfies Meta; export function Normal(): JSX.Element { return ; diff --git a/ts/components/ForwardMessagesModal.stories.tsx b/ts/components/ForwardMessagesModal.stories.tsx index 0cf043ed2d52..a078f4a4ae47 100644 --- a/ts/components/ForwardMessagesModal.stories.tsx +++ b/ts/components/ForwardMessagesModal.stories.tsx @@ -2,10 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; -import { text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import type { AttachmentType } from '../types/Attachment'; import type { PropsType } from './ForwardMessagesModal'; @@ -15,25 +13,25 @@ import { getDefaultConversation } from '../test-both/helpers/getDefaultConversat import { setupI18n } from '../util/setupI18n'; import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext'; import { CompositionTextArea } from './CompositionTextArea'; -import type { MessageForwardDraft } from '../util/maybeForwardMessages'; +import type { MessageForwardDraft } from '../types/ForwardDraft'; const createAttachment = ( props: Partial = {} ): AttachmentType => ({ pending: false, path: 'fileName.jpg', - contentType: stringToMIMEType( - text('attachment contentType', props.contentType || '') - ), - fileName: text('attachment fileName', props.fileName || ''), + contentType: stringToMIMEType(props.contentType ?? ''), + fileName: props.fileName ?? '', screenshotPath: props.pending === false ? props.screenshotPath : undefined, - url: text('attachment url', props.pending === false ? props.url || '' : ''), + url: props.pending === false ? props.url ?? '' : '', size: 3433, }); export default { title: 'Components/ForwardMessageModal', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const i18n = setupI18n('en', enMessages); @@ -82,7 +80,7 @@ function getMessageForwardDraft( attachments: overrideProps.attachments, hasContact: Boolean(overrideProps.hasContact), isSticker: Boolean(overrideProps.isSticker), - messageBody: text('messageBody', overrideProps.messageBody || ''), + messageBody: overrideProps.messageBody ?? '', originalMessageId: '123', previews: overrideProps.previews ?? [], }; @@ -102,10 +100,6 @@ export function WithText(): JSX.Element { ); } -WithText.story = { - name: 'with text', -}; - export function ASticker(): JSX.Element { return ( ); } - -AnnouncementOnlyGroupsNonAdmin.story = { - name: 'announcement only groups non-admin', -}; diff --git a/ts/components/ForwardMessagesModal.tsx b/ts/components/ForwardMessagesModal.tsx index 2675014b56c2..7ea463566fb7 100644 --- a/ts/components/ForwardMessagesModal.tsx +++ b/ts/components/ForwardMessagesModal.tsx @@ -27,11 +27,6 @@ import { shouldNeverBeCalled, asyncShouldNeverBeCalled, } from '../util/shouldNeverBeCalled'; -import type { MessageForwardDraft } from '../util/maybeForwardMessages'; -import { - isDraftEditable, - isDraftForwardable, -} from '../util/maybeForwardMessages'; import type { LinkPreviewType } from '../types/message/LinkPreviews'; import { LinkPreviewSourceType } from '../types/LinkPreview'; import { ToastType } from '../types/Toast'; @@ -41,6 +36,11 @@ import { BodyRange } from '../types/BodyRange'; import { UserText } from './UserText'; import { Modal } from './Modal'; import { SizeObserver } from '../hooks/useSizeObserver'; +import { + isDraftEditable, + isDraftForwardable, + type MessageForwardDraft, +} from '../types/ForwardDraft'; export type DataPropsType = { candidateConversations: ReadonlyArray; diff --git a/ts/components/GroupCallOverflowArea.stories.tsx b/ts/components/GroupCallOverflowArea.stories.tsx index 1bd1272cd5db..17cf8eb16fd6 100644 --- a/ts/components/GroupCallOverflowArea.stories.tsx +++ b/ts/components/GroupCallOverflowArea.stories.tsx @@ -3,9 +3,9 @@ import React from 'react'; import { memoize, times } from 'lodash'; -import { number } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './GroupCallOverflowArea'; import { GroupCallOverflowArea } from './GroupCallOverflowArea'; import { setupI18n } from '../util/setupI18n'; import { getDefaultConversationWithServiceId } from '../test-both/helpers/getDefaultConversation'; @@ -34,7 +34,9 @@ const allRemoteParticipants = times(MAX_PARTICIPANTS).map(index => ({ export default { title: 'Components/GroupCallOverflowArea', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const defaultProps = { getFrameBuffer: memoize(() => Buffer.alloc(FRAME_BUFFER_SIZE)), @@ -68,10 +70,6 @@ export function NoOverflowedParticipants(): JSX.Element { ); } -NoOverflowedParticipants.story = { - name: 'No overflowed participants', -}; - export function OneOverflowedParticipant(): JSX.Element { return ( @@ -83,10 +81,6 @@ export function OneOverflowedParticipant(): JSX.Element { ); } -OneOverflowedParticipant.story = { - name: 'One overflowed participant', -}; - export function ThreeOverflowedParticipants(): JSX.Element { return ( @@ -98,10 +92,6 @@ export function ThreeOverflowedParticipants(): JSX.Element { ); } -ThreeOverflowedParticipants.story = { - name: 'Three overflowed participants', -}; - export function ManyOverflowedParticipants(): JSX.Element { return ( @@ -109,18 +99,9 @@ export function ManyOverflowedParticipants(): JSX.Element { {...defaultProps} overflowedParticipants={allRemoteParticipants.slice( 0, - number('Participant count', MAX_PARTICIPANTS, { - range: true, - min: 0, - max: MAX_PARTICIPANTS, - step: 1, - }) + MAX_PARTICIPANTS )} /> ); } - -ManyOverflowedParticipants.story = { - name: 'Many overflowed participants', -}; diff --git a/ts/components/GroupCallOverflowArea.tsx b/ts/components/GroupCallOverflowArea.tsx index b60a135e0be0..76edcebe6710 100644 --- a/ts/components/GroupCallOverflowArea.tsx +++ b/ts/components/GroupCallOverflowArea.tsx @@ -15,7 +15,7 @@ const OVERFLOW_SCROLL_BUTTON_RATIO = 0.75; // This should be an integer, as sub-pixel widths can cause performance issues. export const OVERFLOW_PARTICIPANT_WIDTH = 140; -type PropsType = { +export type PropsType = { getFrameBuffer: () => Buffer; getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource; i18n: LocalizerType; diff --git a/ts/components/GroupCallRemoteParticipant.stories.tsx b/ts/components/GroupCallRemoteParticipant.stories.tsx index 5d7cc94ef726..3eb13477a897 100644 --- a/ts/components/GroupCallRemoteParticipant.stories.tsx +++ b/ts/components/GroupCallRemoteParticipant.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { memoize, noop } from 'lodash'; -import { select } from '@storybook/addon-knobs'; - +import { memoize } from 'lodash'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './GroupCallRemoteParticipant'; import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; @@ -46,8 +45,9 @@ const createProps = ( } = {} ): PropsType => ({ getFrameBuffer, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getGroupCallVideoFrameSource: noop as any, + getGroupCallVideoFrameSource: () => { + return { receiveVideoFrame: () => undefined }; + }, i18n, audioLevel: 0, remoteParticipant: { @@ -72,7 +72,9 @@ const createProps = ( export default { title: 'Components/GroupCallRemoteParticipant', -}; + argTypes: {}, + args: {}, +} satisfies Meta; export function Default(): JSX.Element { return ( @@ -101,7 +103,7 @@ export function Speaking(): JSX.Element { left: (120 + 10) * index, top: 0, width: 120, - audioLevel: select('audioLevel', [0, 0.5, 1], 0.5), + audioLevel: 0.5, remoteParticipantsCount, }, { hasRemoteAudio: true, presenting } @@ -126,10 +128,6 @@ export function IsInPip(): JSX.Element { ); } -IsInPip.story = { - name: 'isInPip', -}; - export function Blocked(): JSX.Element { return ( ; function Wrapper({ disabled, diff --git a/ts/components/GroupDescriptionInput.tsx b/ts/components/GroupDescriptionInput.tsx index 0534a8d1013b..2a470d819c49 100644 --- a/ts/components/GroupDescriptionInput.tsx +++ b/ts/components/GroupDescriptionInput.tsx @@ -6,7 +6,7 @@ import React, { forwardRef } from 'react'; import { Input } from './Input'; import type { LocalizerType } from '../types/Util'; -type PropsType = { +export type PropsType = { disabled?: boolean; i18n: LocalizerType; onChangeValue: (value: string) => void; diff --git a/ts/components/GroupTitleInput.stories.tsx b/ts/components/GroupTitleInput.stories.tsx index af128f35064b..d873de91de5b 100644 --- a/ts/components/GroupTitleInput.stories.tsx +++ b/ts/components/GroupTitleInput.stories.tsx @@ -2,17 +2,17 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState } from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; - +import type { PropsType } from './GroupTitleInput'; import { GroupTitleInput } from './GroupTitleInput'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/GroupTitleInput', -}; +} satisfies Meta; function Wrapper({ disabled, diff --git a/ts/components/GroupTitleInput.tsx b/ts/components/GroupTitleInput.tsx index 23def66a206d..ebd75e6c68f0 100644 --- a/ts/components/GroupTitleInput.tsx +++ b/ts/components/GroupTitleInput.tsx @@ -6,7 +6,7 @@ import React, { forwardRef } from 'react'; import { Input } from './Input'; import type { LocalizerType } from '../types/Util'; -type PropsType = { +export type PropsType = { disabled?: boolean; i18n: LocalizerType; onChangeValue: (value: string) => void; diff --git a/ts/components/GroupV1MigrationDialog.stories.tsx b/ts/components/GroupV1MigrationDialog.stories.tsx index e86725e4e11d..a6464eb5d97d 100644 --- a/ts/components/GroupV1MigrationDialog.stories.tsx +++ b/ts/components/GroupV1MigrationDialog.stories.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './GroupV1MigrationDialog'; import { GroupV1MigrationDialog } from './GroupV1MigrationDialog'; import type { ConversationType } from '../state/ducks/conversations'; @@ -48,16 +49,12 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/GroupV1MigrationDialog', -}; +} satisfies Meta; export function NotYetMigratedBasic(): JSX.Element { return ; } -NotYetMigratedBasic.story = { - name: 'Not yet migrated, basic', -}; - export function MigratedBasic(): JSX.Element { return ( ); } - -NotYetMigratedJustDroppedMember.story = { - name: 'Not yet migrated, just dropped member', -}; diff --git a/ts/components/GroupV2JoinDialog.stories.tsx b/ts/components/GroupV2JoinDialog.stories.tsx index ab0515616606..6a348fad4f0e 100644 --- a/ts/components/GroupV2JoinDialog.stories.tsx +++ b/ts/components/GroupV2JoinDialog.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { boolean, number, text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './GroupV2JoinDialog'; import { GroupV2JoinDialog } from './GroupV2JoinDialog'; import { setupI18n } from '../util/setupI18n'; @@ -13,13 +12,10 @@ import enMessages from '../../_locales/en/messages.json'; const i18n = setupI18n('en', enMessages); const createProps = (overrideProps: Partial = {}): PropsType => ({ - memberCount: number('memberCount', overrideProps.memberCount || 12), + memberCount: overrideProps.memberCount ?? 12, avatar: overrideProps.avatar, - title: text('title', overrideProps.title || 'Random Group!'), - approvalRequired: boolean( - 'approvalRequired', - overrideProps.approvalRequired || false - ), + title: overrideProps.title ?? 'Random Group!', + approvalRequired: overrideProps.approvalRequired ?? false, groupDescription: overrideProps.groupDescription, join: action('join'), onClose: action('onClose'), @@ -28,7 +24,9 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/GroupV2JoinDialog', -}; + argTypes: {}, + args: {}, +} satisfies Meta; export function Basic(): JSX.Element { return ; @@ -45,10 +43,6 @@ export function ApprovalRequired(): JSX.Element { ); } -ApprovalRequired.story = { - name: 'Approval required', -}; - export function WithAvatar(): JSX.Element { return ( = []; diff --git a/ts/components/InContactsIcon.stories.tsx b/ts/components/InContactsIcon.stories.tsx index 5c5acb63234a..42db8da1b25f 100644 --- a/ts/components/InContactsIcon.stories.tsx +++ b/ts/components/InContactsIcon.stories.tsx @@ -2,16 +2,17 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; +import type { PropsType } from './InContactsIcon'; import { InContactsIcon } from './InContactsIcon'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/InContactsIcon', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/InContactsIcon.tsx b/ts/components/InContactsIcon.tsx index 9ee209a45ca2..e0cfe0d218c0 100644 --- a/ts/components/InContactsIcon.tsx +++ b/ts/components/InContactsIcon.tsx @@ -7,7 +7,7 @@ import classNames from 'classnames'; import { Tooltip } from './Tooltip'; import type { LocalizerType } from '../types/Util'; -type PropsType = { +export type PropsType = { className?: string; tooltipContainerRef?: React.RefObject; i18n: LocalizerType; diff --git a/ts/components/Inbox.stories.tsx b/ts/components/Inbox.stories.tsx index 213150438310..b2f93e4dfc79 100644 --- a/ts/components/Inbox.stories.tsx +++ b/ts/components/Inbox.stories.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState, useEffect, useMemo } from 'react'; -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import { noop } from 'lodash'; import { Inbox } from './Inbox'; @@ -16,41 +16,15 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Inbox', - argTypes: { - i18n: { - defaultValue: i18n, - }, - hasInitialLoadCompleted: { - defaultValue: false, - }, - daysAgo: { - control: 'select', - defaultValue: undefined, - options: [undefined, 1, 2, 3, 7, 14, 21], - }, - isCustomizingPreferredReactions: { - defaultValue: false, - }, - onConversationClosed: { - action: true, - }, - onConversationOpened: { - action: true, - }, - scrollToMessage: { - action: true, - }, - showConversation: { - action: true, - }, - showWhatsNewModal: { - action: true, - }, + args: { + i18n, + hasInitialLoadCompleted: false, + isCustomizingPreferredReactions: false, }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = ({ +const Template: StoryFn = ({ daysAgo, ...args }) => { @@ -90,6 +64,3 @@ const Template: Story = ({ }; export const Default = Template.bind({}); -Default.story = { - name: 'Default', -}; diff --git a/ts/components/IncomingCallBar.stories.tsx b/ts/components/IncomingCallBar.stories.tsx index 0228afebcaac..099e55c19c34 100644 --- a/ts/components/IncomingCallBar.stories.tsx +++ b/ts/components/IncomingCallBar.stories.tsx @@ -3,7 +3,8 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './IncomingCallBar'; import { IncomingCallBar } from './IncomingCallBar'; import { CallMode } from '../types/Calling'; import { setupI18n } from '../util/setupI18n'; @@ -53,7 +54,7 @@ const groupConversation = getDefaultConversation({ export default { title: 'Components/IncomingCallBar', -}; +} satisfies Meta; export function IncomingDirectCallVideo(): JSX.Element { return ( @@ -66,10 +67,6 @@ export function IncomingDirectCallVideo(): JSX.Element { ); } -IncomingDirectCallVideo.story = { - name: 'Incoming direct call (video)', -}; - export function IncomingDirectCallAudio(): JSX.Element { return ( ); } - -IncomingGroupCallCallingYouAnd4Others.story = { - name: 'Incoming group call (calling you and 4 others)', -}; diff --git a/ts/components/Input.stories.tsx b/ts/components/Input.stories.tsx index 370358fa57c6..e6b580e9014e 100644 --- a/ts/components/Input.stories.tsx +++ b/ts/components/Input.stories.tsx @@ -2,10 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState } from 'react'; - -import { text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './Input'; import { Input } from './Input'; import { setupI18n } from '../util/setupI18n'; @@ -15,7 +13,9 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Input', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const createProps = (overrideProps: Partial = {}): PropsType => ({ disabled: Boolean(overrideProps.disabled), @@ -26,11 +26,8 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ icon: overrideProps.icon, maxLengthCount: overrideProps.maxLengthCount, onChange: action('onChange'), - placeholder: text( - 'placeholder', - overrideProps.placeholder || 'Enter some text here' - ), - value: text('value', overrideProps.value || ''), + placeholder: overrideProps.placeholder ?? 'Enter some text here', + value: overrideProps.value ?? '', whenToShowRemainingCount: overrideProps.whenToShowRemainingCount, }); @@ -55,10 +52,6 @@ export function HasClearButton(): JSX.Element { ); } -HasClearButton.story = { - name: 'hasClearButton', -}; - export function CharacterCount(): JSX.Element { return ( ); } - -SpellcheckDisabled.story = { - name: 'spellcheck disabled', -}; diff --git a/ts/components/Intl.stories.tsx b/ts/components/Intl.stories.tsx index d2dfb2359b90..1460c902ddf1 100644 --- a/ts/components/Intl.stories.tsx +++ b/ts/components/Intl.stories.tsx @@ -1,7 +1,7 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import * as React from 'react'; import type { Props } from './Intl'; @@ -14,7 +14,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Intl', component: Intl, -} as Meta; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ i18n, @@ -24,7 +24,7 @@ const createProps = (overrideProps: Partial = {}): Props => ({ // eslint-disable-next-line max-len // eslint-disable-next-line react/function-component-definition, local-rules/valid-i18n-keys -const Template: Story = args => ; +const Template: StoryFn = args => ; export const NoReplacements = Template.bind({}); NoReplacements.args = createProps({ diff --git a/ts/components/LeftPane.stories.tsx b/ts/components/LeftPane.stories.tsx index 3b95675eef77..4c27e6c69fb6 100644 --- a/ts/components/LeftPane.stories.tsx +++ b/ts/components/LeftPane.stories.tsx @@ -2,10 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; -import { boolean, select } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './LeftPane'; import { LeftPane, LeftPaneMode } from './LeftPane'; import { CaptchaDialog } from './CaptchaDialog'; @@ -45,7 +43,9 @@ type OverridePropsType = Partial & { export default { title: 'Components/LeftPane', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const defaultConversations: Array = [ getDefaultConversation({ @@ -126,11 +126,8 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => { }; } - const isUpdateDownloaded = boolean('isUpdateDownloaded', false); - const isContactManagementEnabled = boolean( - 'isContactManagementEnabled', - true - ); + const isUpdateDownloaded = false; + const isContactManagementEnabled = true; return { otherTabsUnreadStats: { @@ -151,28 +148,20 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => { hasFailedStorySends: false, hasPendingUpdate: false, i18n, - isMacOS: boolean('isMacOS', false), + isMacOS: false, preferredWidthFromStorage: 320, regionCode: 'US', - challengeStatus: select( - 'challengeStatus', - ['idle', 'required', 'pending'], - 'idle' - ), - crashReportCount: select('challengeReportCount', [0, 1], 0), + challengeStatus: 'idle', + crashReportCount: 0, - hasNetworkDialog: boolean('hasNetworkDialog', false), - hasExpiredDialog: boolean('hasExpiredDialog', false), - hasRelinkDialog: boolean('hasRelinkDialog', false), - hasUpdateDialog: boolean('hasUpdateDialog', false), - unsupportedOSDialogType: select( - 'unsupportedOSDialogType', - ['error', 'warning', undefined], - undefined - ), + hasNetworkDialog: false, + hasExpiredDialog: false, + hasRelinkDialog: false, + hasUpdateDialog: false, + unsupportedOSDialogType: undefined, isUpdateDownloaded, isContactManagementEnabled, - navTabsCollapsed: boolean('navTabsCollapsed', false), + navTabsCollapsed: false, setChallengeStatus: action('setChallengeStatus'), lookupConversationWithoutServiceId: @@ -315,10 +304,6 @@ export function InboxNoConversations(): JSX.Element { ); } -InboxNoConversations.story = { - name: 'Inbox: no conversations', -}; - export function InboxOnlyPinnedConversations(): JSX.Element { return ( ; } -InboxPinnedNonPinnedAndArchivedConversations.story = { - name: 'Inbox: pinned, non-pinned, and archived conversations', -}; - export function SearchNoResultsWhenSearchingEverywhere(): JSX.Element { return ( ( - -); - -_CrashReportDialog.story = { - name: 'Crash report dialog', -}; +export function _CrashReportDialog(): JSX.Element { + return ( + + ); +} export function ChooseGroupMembersPartialPhoneNumber(): JSX.Element { return ( @@ -1055,10 +910,6 @@ export function ChooseGroupMembersPartialPhoneNumber(): JSX.Element { ); } -ChooseGroupMembersPartialPhoneNumber.story = { - name: 'Choose Group Members: Partial phone number', -}; - export function ChooseGroupMembersValidPhoneNumber(): JSX.Element { return ( null, clickLabel: 'Click me', containerWidthBreakpoint: WidthBreakpoint.Wide, }, -}; +} satisfies Meta; export const Update = { args: { diff --git a/ts/components/Lightbox.stories.tsx b/ts/components/Lightbox.stories.tsx index 5f600473ec16..a6c55eb72c29 100644 --- a/ts/components/Lightbox.stories.tsx +++ b/ts/components/Lightbox.stories.tsx @@ -2,11 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState } from 'react'; - import { action } from '@storybook/addon-actions'; -import { number } from '@storybook/addon-knobs'; import { noop } from 'lodash'; - +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import type { PropsType } from './Lightbox'; import { Lightbox } from './Lightbox'; @@ -26,7 +24,9 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Lightbox', -}; + argTypes: {}, + args: {}, +} satisfies Meta; type OverridePropsMediaItemType = Partial & { caption?: string }; @@ -57,9 +57,7 @@ function createMediaItem( const createProps = (overrideProps: Partial = {}): PropsType => { // eslint-disable-next-line react-hooks/rules-of-hooks - const [selectedIndex, setSelectedIndex] = useState( - number('selectedIndex', overrideProps.selectedIndex || 0) - ); + const [selectedIndex, setSelectedIndex] = useState(0); const media = overrideProps.media || []; return { closeLightbox: action('closeLightbox'), @@ -196,10 +194,6 @@ export function ImageWithCaptionNormalImage(): JSX.Element { ); } -ImageWithCaptionNormalImage.story = { - name: 'Image with Caption (normal image)', -}; - export function ImageWithCaptionAllWhiteImage(): JSX.Element { return ( { + log.error('Lightbox: Failed to play video', Errors.toLogFormat(error)); + }); } else { videoElement.pause(); } diff --git a/ts/components/ListTile.stories.tsx b/ts/components/ListTile.stories.tsx index 279c8cbf1327..b54c376b7b76 100644 --- a/ts/components/ListTile.stories.tsx +++ b/ts/components/ListTile.stories.tsx @@ -1,7 +1,7 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; import { ListTile } from './ListTile'; import type { Props } from './ListTile'; @@ -11,12 +11,12 @@ import { UserText } from './UserText'; export default { title: 'Components/ListTile', component: ListTile, -}; +} satisfies Meta; const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam feugiat quam vitae semper facilisis. Praesent eu efficitur dui. Donec semper mattis nisl non hendrerit.'; -function TemplateList(width: number): Story { +function TemplateList(width: number): StoryFn { // eslint-disable-next-line react/display-name return args => { return ( diff --git a/ts/components/MediaEditor.stories.tsx b/ts/components/MediaEditor.stories.tsx index 53ec34c3f030..7ac7928a5213 100644 --- a/ts/components/MediaEditor.stories.tsx +++ b/ts/components/MediaEditor.stories.tsx @@ -1,9 +1,10 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './MediaEditor'; import { MediaEditor } from './MediaEditor'; import enMessages from '../../_locales/en/messages.json'; @@ -19,48 +20,28 @@ const IMAGE_4 = '/fixtures/snow.jpg'; export default { title: 'Components/MediaEditor', component: MediaEditor, - argTypes: { - getPreferredBadge: { action: true }, - i18n: { - defaultValue: i18n, - }, - imageToBlurHash: { action: true }, - imageSrc: { - defaultValue: IMAGE_2, - }, - installedPacks: { - defaultValue: installedPacks, - }, - isFormattingEnabled: { - defaultValue: true, - }, - isFormattingFlagEnabled: { - defaultValue: true, - }, - isFormattingSpoilersFlagEnabled: { - defaultValue: true, - }, - isSending: { - defaultValue: false, - }, - onClose: { action: true }, - onDone: { action: true }, - onPickEmoji: { action: true }, - onTextTooLong: { action: true }, - platform: { - defaultValue: 'darwin', - }, - recentStickers: { - defaultValue: [Stickers.wide, Stickers.tall, Stickers.abe], - }, - skinTone: { - defaultValue: 0, - }, + args: { + getPreferredBadge: () => undefined, + i18n, + imageToBlurHash: input => Promise.resolve(input.toString()), + imageSrc: IMAGE_2, + installedPacks, + isFormattingEnabled: true, + isFormattingFlagEnabled: true, + isFormattingSpoilersFlagEnabled: true, + isSending: false, + onClose: action('onClose'), + onDone: action('onDone'), + onPickEmoji: action('onPickEmoji'), + onTextTooLong: action('onTextTooLong'), + platform: 'darwin', + recentStickers: [Stickers.wide, Stickers.tall, Stickers.abe], + skinTone: 0, }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const ExtraLarge = Template.bind({}); diff --git a/ts/components/MediaQualitySelector.stories.tsx b/ts/components/MediaQualitySelector.stories.tsx index a1277f3516a3..61ba742e1efd 100644 --- a/ts/components/MediaQualitySelector.stories.tsx +++ b/ts/components/MediaQualitySelector.stories.tsx @@ -2,10 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - import { action } from '@storybook/addon-actions'; -import { boolean } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import type { PropsType } from './MediaQualitySelector'; import { MediaQualitySelector } from './MediaQualitySelector'; @@ -13,14 +11,16 @@ import { setupI18n } from '../util/setupI18n'; export default { title: 'Components/MediaQualitySelector', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const i18n = setupI18n('en', enMessages); const createProps = (overrideProps: Partial = {}): PropsType => ({ conversationId: 'abc123', i18n, - isHighQuality: boolean('isHighQuality', Boolean(overrideProps.isHighQuality)), + isHighQuality: overrideProps.isHighQuality ?? false, onSelectQuality: action('onSelectQuality'), }); diff --git a/ts/components/MiniPlayer.stories.tsx b/ts/components/MiniPlayer.stories.tsx index 7c5e58cc89c0..f1570e3b1d30 100644 --- a/ts/components/MiniPlayer.stories.tsx +++ b/ts/components/MiniPlayer.stories.tsx @@ -2,6 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useEffect, useState } from 'react'; +import type { Meta } from '@storybook/react'; +import type { Props } from './MiniPlayer'; import { MiniPlayer, PlayerState } from './MiniPlayer'; import { setupI18n } from '../util/setupI18n'; @@ -15,7 +17,7 @@ audio.src = '/fixtures/incompetech-com-Agnus-Dei-X.mp3'; export default { title: 'components/MiniPlayer', component: MiniPlayer, -}; +} satisfies Meta; export function Default(): JSX.Element { const [active, setActive] = useState(false); diff --git a/ts/components/Modal.stories.tsx b/ts/components/Modal.stories.tsx index 267fdffeb5c1..6a4034febc5b 100644 --- a/ts/components/Modal.stories.tsx +++ b/ts/components/Modal.stories.tsx @@ -3,19 +3,19 @@ import React from 'react'; import { noop } from 'lodash'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; import { Button } from './Button'; +import type { ModalPropsType } from './Modal'; import { Modal } from './Modal'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Modal', -}; +} satisfies Meta; const onClose = action('onClose'); @@ -30,10 +30,6 @@ export function BareBonesShort(): JSX.Element { ); } -BareBonesShort.story = { - name: 'Bare bones, short', -}; - export function BareBonesLong(): JSX.Element { return ( @@ -45,10 +41,6 @@ export function BareBonesLong(): JSX.Element { ); } -BareBonesLong.story = { - name: 'Bare bones, long', -}; - export function BareBonesLongWithButton(): JSX.Element { return ( ); } - -WithBackButton.story = { - name: 'Back Button', -}; diff --git a/ts/components/MyStories.stories.tsx b/ts/components/MyStories.stories.tsx index 8d916eff8617..915fae063149 100644 --- a/ts/components/MyStories.stories.tsx +++ b/ts/components/MyStories.stories.tsx @@ -1,13 +1,14 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, ReactFramework, Story } from '@storybook/react'; +import type { Meta, ReactRenderer, StoryFn } from '@storybook/react'; import type { PlayFunction } from '@storybook/csf'; import React from 'react'; -import { expect } from '@storybook/jest'; +import { expect, jest } from '@storybook/jest'; import { v4 as uuid } from 'uuid'; import { within, userEvent } from '@storybook/testing-library'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './MyStories'; import enMessages from '../../_locales/en/messages.json'; import { MY_STORY_ID } from '../types/Stories'; @@ -24,50 +25,32 @@ export default { title: 'Components/MyStories', component: MyStories, argTypes: { - i18n: { - defaultValue: i18n, - }, - onBack: { - action: true, - }, - onDelete: { - action: true, - }, - onForward: { - action: true, - }, - onSave: { - action: true, - }, - ourConversationId: { - defaultValue: getDefaultConversation().id, - }, hasViewReceiptSetting: { control: 'boolean', - defaultValue: false, }, - queueStoryDownload: { - action: true, - }, - retryMessageSend: { - action: true, - }, - viewStory: { action: true }, }, -} as Meta; + args: { + i18n, + onBack: jest.fn(action('onBack')), + onDelete: action('onDelete'), + onForward: jest.fn(action('onForward')), + onSave: jest.fn(action('onSave')), + hasViewReceiptSetting: false, + queueStoryDownload: action('queueStoryDownload'), + retryMessageSend: action('retryMessageSend'), + viewStory: action('viewStory'), + }, +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const NoStories = Template.bind({}); NoStories.args = { myStories: [], }; -NoStories.story = { - name: 'No Stories', -}; -const interactionTest: PlayFunction = async ({ +const interactionTest: PlayFunction = async ({ args, canvasElement, }) => { @@ -76,7 +59,7 @@ const interactionTest: PlayFunction = async ({ await userEvent.click(btnDownload); await expect(args.onSave).toHaveBeenCalled(); - const [btnBack] = canvas.getAllByLabelText('Back'); + const btnBack = canvas.getByText('Back'); await userEvent.click(btnBack); await expect(args.onBack).toHaveBeenCalled(); @@ -94,9 +77,6 @@ SingleListStories.args = { myStories: [getFakeMyStory(MY_STORY_ID)], }; SingleListStories.play = interactionTest; -SingleListStories.story = { - name: 'One distribution list', -}; export const MultiListStories = Template.bind({}); MultiListStories.args = { @@ -107,9 +87,6 @@ MultiListStories.args = { ], }; MultiListStories.play = interactionTest; -MultiListStories.story = { - name: 'Multiple distribution lists', -}; export const FailedSentStory = Template.bind({}); { diff --git a/ts/components/MyStoryButton.stories.tsx b/ts/components/MyStoryButton.stories.tsx index b49b3b057741..c4589f0e24ed 100644 --- a/ts/components/MyStoryButton.stories.tsx +++ b/ts/components/MyStoryButton.stories.tsx @@ -1,12 +1,13 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, ReactFramework, Story } from '@storybook/react'; +import type { Meta, ReactRenderer, StoryFn } from '@storybook/react'; import type { PlayFunction } from '@storybook/csf'; import React from 'react'; -import { expect } from '@storybook/jest'; +import { expect, jest } from '@storybook/jest'; import { within, userEvent } from '@storybook/testing-library'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './MyStoryButton'; import enMessages from '../../_locales/en/messages.json'; import { MyStoryButton } from './MyStoryButton'; @@ -21,27 +22,21 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/MyStoriesButton', component: MyStoryButton, - argTypes: { - i18n: { - defaultValue: i18n, - }, - me: { - defaultValue: getDefaultConversation(), - }, - myStories: { - defaultValue: [getFakeMyStory()], - }, - onAddStory: { action: true }, - onClick: { action: true }, - queueStoryDownload: { action: true }, - showToast: { action: true }, + args: { + i18n, + me: getDefaultConversation(), + myStories: [getFakeMyStory()], + onAddStory: jest.fn(action('onAddStory')), + onClick: jest.fn(action('onClick')), + queueStoryDownload: action('queueStoryDownload'), + showToast: action('showToast'), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; -const interactionTest: PlayFunction = async ({ +const interactionTest: PlayFunction = async ({ args, canvasElement, }) => { @@ -51,40 +46,34 @@ const interactionTest: PlayFunction = async ({ const textStory = canvas.getByText('Text story'); await userEvent.click(textStory); await expect(args.onAddStory).toHaveBeenCalled(); - const btnStory = canvas.getByText('My Stories'); - await userEvent.click(btnStory); - await expect(args.onClick).toHaveBeenCalled(); + if (args.myStories.length > 0) { + const btnStory = canvas.getByText('My Stories'); + await userEvent.click(btnStory); + await expect(args.onClick).toHaveBeenCalled(); + } }; export const NoStory = Template.bind({}); NoStory.args = { myStories: [], }; -NoStory.story = { - name: 'No Story', -}; + NoStory.play = interactionTest; export const OneStory = Template.bind({}); OneStory.args = {}; -OneStory.story = { - name: 'One Story', -}; + OneStory.play = interactionTest; export const ManyStories = Template.bind({}); ManyStories.args = { myStories: [getFakeMyStory(), getFakeMyStory()], }; -ManyStories.story = { - name: 'Many Stories', -}; + ManyStories.play = interactionTest; export const SendingStory = Template.bind({}); -SendingStory.story = { - name: 'Sending Story', -}; + { const myStory = getFakeMyStory(); SendingStory.args = { @@ -115,9 +104,7 @@ SendingStory.story = { SendingStory.play = interactionTest; export const FailedSendStory = Template.bind({}); -FailedSendStory.story = { - name: 'Failed Send Story', -}; + { const myStory = getFakeMyStory(); FailedSendStory.args = { diff --git a/ts/components/NewlyCreatedGroupInvitedContactsDialog.stories.tsx b/ts/components/NewlyCreatedGroupInvitedContactsDialog.stories.tsx index a5034790240d..c4c458e93239 100644 --- a/ts/components/NewlyCreatedGroupInvitedContactsDialog.stories.tsx +++ b/ts/components/NewlyCreatedGroupInvitedContactsDialog.stories.tsx @@ -5,6 +5,8 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './NewlyCreatedGroupInvitedContactsDialog'; import { NewlyCreatedGroupInvitedContactsDialog } from './NewlyCreatedGroupInvitedContactsDialog'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -21,7 +23,7 @@ const conversations: Array = [ export default { title: 'Components/NewlyCreatedGroupInvitedContactsDialog', -}; +} satisfies Meta; export function OneContact(): JSX.Element { return ( @@ -35,10 +37,6 @@ export function OneContact(): JSX.Element { ); } -OneContact.story = { - name: 'One contact', -}; - export function TwoContacts(): JSX.Element { return ( ); } - -TwoContacts.story = { - name: 'Two contacts', -}; diff --git a/ts/components/NewlyCreatedGroupInvitedContactsDialog.tsx b/ts/components/NewlyCreatedGroupInvitedContactsDialog.tsx index e0c17032d7eb..51887d427ae3 100644 --- a/ts/components/NewlyCreatedGroupInvitedContactsDialog.tsx +++ b/ts/components/NewlyCreatedGroupInvitedContactsDialog.tsx @@ -12,7 +12,7 @@ import { ContactName } from './conversation/ContactName'; import { GroupDialog } from './GroupDialog'; import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser'; -type PropsType = { +export type PropsType = { contacts: Array; getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; diff --git a/ts/components/OutgoingGiftBadgeModal.stories.tsx b/ts/components/OutgoingGiftBadgeModal.stories.tsx index 8c9e22d44a3a..d5a85f5eab44 100644 --- a/ts/components/OutgoingGiftBadgeModal.stories.tsx +++ b/ts/components/OutgoingGiftBadgeModal.stories.tsx @@ -2,12 +2,10 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './OutgoingGiftBadgeModal'; import { OutgoingGiftBadgeModal } from './OutgoingGiftBadgeModal'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; import { BadgeCategory } from '../badges/BadgeCategory'; @@ -30,11 +28,8 @@ const getPreferredBadge = () => ({ }); const createProps = (overrideProps: Partial = {}): PropsType => ({ - recipientTitle: text( - 'recipientTitle', - overrideProps.recipientTitle || 'Default Name' - ), - badgeId: text('badgeId', overrideProps.badgeId || 'heart'), + recipientTitle: overrideProps.recipientTitle ?? 'Default Name', + badgeId: overrideProps.badgeId ?? 'heart', getPreferredBadge, hideOutgoingGiftBadgeModal: action('hideOutgoingGiftBadgeModal'), i18n, @@ -42,7 +37,9 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ export default { title: 'Components/OutgoingGiftBadgeModal', -}; + argTypes: {}, + args: {}, +} satisfies Meta; export function Normal(): JSX.Element { return ; @@ -56,7 +53,3 @@ export function MissingBadge(): JSX.Element { return ; } - -MissingBadge.story = { - name: 'Missing badge', -}; diff --git a/ts/components/PlaybackButton.stories.tsx b/ts/components/PlaybackButton.stories.tsx index 8080e83822fb..2ae8bffea444 100644 --- a/ts/components/PlaybackButton.stories.tsx +++ b/ts/components/PlaybackButton.stories.tsx @@ -4,12 +4,14 @@ import React from 'react'; import type { CSSProperties } from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { ButtonProps } from './PlaybackButton'; import { PlaybackButton } from './PlaybackButton'; export default { title: 'components/PlaybackButton', component: PlaybackButton, -}; +} satisfies Meta; const rowStyles: CSSProperties = { display: 'flex', diff --git a/ts/components/PlaybackButton.tsx b/ts/components/PlaybackButton.tsx index 0f9794408833..c66ac08aa86c 100644 --- a/ts/components/PlaybackButton.tsx +++ b/ts/components/PlaybackButton.tsx @@ -12,7 +12,7 @@ const SPRING_CONFIG = { velocity: 0.01, }; -type ButtonProps = { +export type ButtonProps = { context?: 'incoming' | 'outgoing'; variant: 'message' | 'mini' | 'draft'; mod: 'play' | 'pause' | 'download' | 'pending'; diff --git a/ts/components/Preferences.stories.tsx b/ts/components/Preferences.stories.tsx index 88dcb62e5c33..2d6f975d24df 100644 --- a/ts/components/Preferences.stories.tsx +++ b/ts/components/Preferences.stories.tsx @@ -1,17 +1,17 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import enMessages from '../../_locales/en/messages.json'; -import type { PropsDataType, PropsType } from './Preferences'; +import type { PropsType } from './Preferences'; import { Preferences } from './Preferences'; import { setupI18n } from '../util/setupI18n'; import { DEFAULT_CONVERSATION_COLOR } from '../types/Colors'; import { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode'; import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability'; -import { objectMap } from '../util/objectMap'; import { DurationInSeconds } from '../util/durations'; const i18n = setupI18n('en', enMessages); @@ -44,145 +44,144 @@ const availableSpeakers = [ }, ]; -const getDefaultArgs = (): PropsDataType => ({ - availableCameras: [ - { - deviceId: - 'dfbe6effe70b0611ba0fdc2a9ea3f39f6cb110e6687948f7e5f016c111b7329c', - groupId: - '63ee218d2446869e40adfc958ff98263e51f74382b0143328ee4826f20a76f47', - kind: 'videoinput' as MediaDeviceKind, - label: 'FaceTime HD Camera (Built-in) (9fba:bced)', - }, - { - deviceId: - 'e2db196a31d50ff9b135299dc0beea67f65b1a25a06d8a4ce76976751bb7a08d', - groupId: - '218ba7f00d7b1239cca15b9116769e5e7d30cc01104ebf84d667643661e0ecf9', - kind: 'videoinput' as MediaDeviceKind, - label: 'Logitech Webcam (4e72:9058)', - }, - ], - availableMicrophones, - availableSpeakers, - blockedCount: 0, - customColors: {}, - defaultConversationColor: DEFAULT_CONVERSATION_COLOR, - deviceName: 'Work Windows ME', - hasAudioNotifications: true, - hasAutoDownloadUpdate: true, - hasAutoLaunch: true, - hasCallNotifications: true, - hasCallRingtoneNotification: false, - hasCountMutedConversations: false, - hasCustomTitleBar: true, - hasHideMenuBar: false, - hasIncomingCallNotifications: true, - hasLinkPreviews: true, - hasMediaCameraPermissions: true, - hasMediaPermissions: true, - hasMessageAudio: true, - hasMinimizeToAndStartInSystemTray: true, - hasMinimizeToSystemTray: true, - hasNotificationAttention: false, - hasNotifications: true, - hasReadReceipts: true, - hasRelayCalls: false, - hasSpellCheck: true, - hasStoriesDisabled: false, - hasTextFormatting: true, - hasTypingIndicators: true, - initialSpellCheckSetting: true, - isAutoDownloadUpdatesSupported: true, - isAutoLaunchSupported: true, - isFormattingFlagEnabled: true, - isHideMenuBarSupported: true, - isNotificationAttentionSupported: true, - isPhoneNumberSharingSupported: true, - isSyncSupported: true, - isSystemTraySupported: true, - isMinimizeToAndStartInSystemTraySupported: true, - lastSyncTime: Date.now(), - notificationContent: 'name', - selectedCamera: - 'dfbe6effe70b0611ba0fdc2a9ea3f39f6cb110e6687948f7e5f016c111b7329c', - selectedMicrophone: availableMicrophones[0], - selectedSpeaker: availableSpeakers[1], - shouldShowStoriesSettings: true, - sentMediaQualitySetting: 'standard', - themeSetting: 'system', - universalExpireTimer: DurationInSeconds.HOUR, - whoCanFindMe: PhoneNumberDiscoverability.Discoverable, - whoCanSeeMe: PhoneNumberSharingMode.Everybody, - zoomFactor: 1, -}); - -const defaultArgTypes: Record = {}; -objectMap(getDefaultArgs(), (key, defaultValue) => { - defaultArgTypes[key] = { defaultValue }; -}); - export default { title: 'Components/Preferences', component: Preferences, - argTypes: { - // ...defaultArgTypes, + args: { + i18n, - i18n: { - defaultValue: i18n, - }, + availableCameras: [ + { + deviceId: + 'dfbe6effe70b0611ba0fdc2a9ea3f39f6cb110e6687948f7e5f016c111b7329c', + groupId: + '63ee218d2446869e40adfc958ff98263e51f74382b0143328ee4826f20a76f47', + kind: 'videoinput' as MediaDeviceKind, + label: 'FaceTime HD Camera (Built-in) (9fba:bced)', + }, + { + deviceId: + 'e2db196a31d50ff9b135299dc0beea67f65b1a25a06d8a4ce76976751bb7a08d', + groupId: + '218ba7f00d7b1239cca15b9116769e5e7d30cc01104ebf84d667643661e0ecf9', + kind: 'videoinput' as MediaDeviceKind, + label: 'Logitech Webcam (4e72:9058)', + }, + ], + availableMicrophones, + availableSpeakers, + blockedCount: 0, + customColors: {}, + defaultConversationColor: DEFAULT_CONVERSATION_COLOR, + deviceName: 'Work Windows ME', + hasAudioNotifications: true, + hasAutoDownloadUpdate: true, + hasAutoLaunch: true, + hasCallNotifications: true, + hasCallRingtoneNotification: false, + hasCountMutedConversations: false, + hasCustomTitleBar: true, + hasHideMenuBar: false, + hasIncomingCallNotifications: true, + hasLinkPreviews: true, + hasMediaCameraPermissions: true, + hasMediaPermissions: true, + hasMessageAudio: true, + hasMinimizeToAndStartInSystemTray: true, + hasMinimizeToSystemTray: true, + hasNotificationAttention: false, + hasNotifications: true, + hasReadReceipts: true, + hasRelayCalls: false, + hasSpellCheck: true, + hasStoriesDisabled: false, + hasTextFormatting: true, + hasTypingIndicators: true, + initialSpellCheckSetting: true, + isAutoDownloadUpdatesSupported: true, + isAutoLaunchSupported: true, + isFormattingFlagEnabled: true, + isHideMenuBarSupported: true, + isNotificationAttentionSupported: true, + isPhoneNumberSharingSupported: true, + isSyncSupported: true, + isSystemTraySupported: true, + isMinimizeToAndStartInSystemTraySupported: true, + lastSyncTime: Date.now(), + notificationContent: 'name', + selectedCamera: + 'dfbe6effe70b0611ba0fdc2a9ea3f39f6cb110e6687948f7e5f016c111b7329c', + selectedMicrophone: availableMicrophones[0], + selectedSpeaker: availableSpeakers[1], + shouldShowStoriesSettings: true, + sentMediaQualitySetting: 'standard', + themeSetting: 'system', + universalExpireTimer: DurationInSeconds.HOUR, + whoCanFindMe: PhoneNumberDiscoverability.Discoverable, + whoCanSeeMe: PhoneNumberSharingMode.Everybody, + zoomFactor: 1, - addCustomColor: { action: true }, - closeSettings: { action: true }, - doDeleteAllData: { action: true }, - doneRendering: { action: true }, - editCustomColor: { action: true }, - executeMenuRole: { action: true }, - getConversationsWithCustomColor: { action: true }, - makeSyncRequest: { action: true }, - onAudioNotificationsChange: { action: true }, - onAutoDownloadUpdateChange: { action: true }, - onAutoLaunchChange: { action: true }, - onCallNotificationsChange: { action: true }, - onCallRingtoneNotificationChange: { action: true }, - onCountMutedConversationsChange: { action: true }, - onHasStoriesDisabledChanged: { action: true }, - onHideMenuBarChange: { action: true }, - onIncomingCallNotificationsChange: { action: true }, - onLastSyncTimeChange: { action: true }, - onMediaCameraPermissionsChange: { action: true }, - onMediaPermissionsChange: { action: true }, - onMessageAudioChange: { action: true }, - onMinimizeToAndStartInSystemTrayChange: { action: true }, - onMinimizeToSystemTrayChange: { action: true }, - onNotificationAttentionChange: { action: true }, - onNotificationContentChange: { action: true }, - onNotificationsChange: { action: true }, - onRelayCallsChange: { action: true }, - onSelectedCameraChange: { action: true }, - onSelectedMicrophoneChange: { action: true }, - onSelectedSpeakerChange: { action: true }, - onSentMediaQualityChange: { action: true }, - onSpellCheckChange: { action: true }, - onTextFormattingChange: { action: true }, - onThemeChange: { action: true }, - onUniversalExpireTimerChange: { action: true }, - onWhoCanSeeMeChange: { action: true }, - onWhoCanFindMeChange: { action: true }, - onZoomFactorChange: { action: true }, - removeCustomColor: { action: true }, - removeCustomColorOnConversations: { action: true }, - resetAllChatColors: { action: true }, - resetDefaultChatColor: { action: true }, - setGlobalDefaultConversationColor: { action: true }, - }, -} as Meta; + getConversationsWithCustomColor: () => Promise.resolve([]), + + addCustomColor: action('addCustomColor'), + closeSettings: action('closeSettings'), + doDeleteAllData: action('doDeleteAllData'), + doneRendering: action('doneRendering'), + editCustomColor: action('editCustomColor'), + executeMenuRole: action('executeMenuRole'), + makeSyncRequest: action('makeSyncRequest'), + onAudioNotificationsChange: action('onAudioNotificationsChange'), + onAutoDownloadUpdateChange: action('onAutoDownloadUpdateChange'), + onAutoLaunchChange: action('onAutoLaunchChange'), + onCallNotificationsChange: action('onCallNotificationsChange'), + onCallRingtoneNotificationChange: action( + 'onCallRingtoneNotificationChange' + ), + onCountMutedConversationsChange: action('onCountMutedConversationsChange'), + onHasStoriesDisabledChanged: action('onHasStoriesDisabledChanged'), + onHideMenuBarChange: action('onHideMenuBarChange'), + onIncomingCallNotificationsChange: action( + 'onIncomingCallNotificationsChange' + ), + onLastSyncTimeChange: action('onLastSyncTimeChange'), + onMediaCameraPermissionsChange: action('onMediaCameraPermissionsChange'), + onMediaPermissionsChange: action('onMediaPermissionsChange'), + onMessageAudioChange: action('onMessageAudioChange'), + onMinimizeToAndStartInSystemTrayChange: action( + 'onMinimizeToAndStartInSystemTrayChange' + ), + onMinimizeToSystemTrayChange: action('onMinimizeToSystemTrayChange'), + onNotificationAttentionChange: action('onNotificationAttentionChange'), + onNotificationContentChange: action('onNotificationContentChange'), + onNotificationsChange: action('onNotificationsChange'), + onRelayCallsChange: action('onRelayCallsChange'), + onSelectedCameraChange: action('onSelectedCameraChange'), + onSelectedMicrophoneChange: action('onSelectedMicrophoneChange'), + onSelectedSpeakerChange: action('onSelectedSpeakerChange'), + onSentMediaQualityChange: action('onSentMediaQualityChange'), + onSpellCheckChange: action('onSpellCheckChange'), + onTextFormattingChange: action('onTextFormattingChange'), + onThemeChange: action('onThemeChange'), + onUniversalExpireTimerChange: action('onUniversalExpireTimerChange'), + onWhoCanSeeMeChange: action('onWhoCanSeeMeChange'), + onWhoCanFindMeChange: action('onWhoCanFindMeChange'), + onZoomFactorChange: action('onZoomFactorChange'), + removeCustomColor: action('removeCustomColor'), + removeCustomColorOnConversations: action( + 'removeCustomColorOnConversations' + ), + resetAllChatColors: action('resetAllChatColors'), + resetDefaultChatColor: action('resetDefaultChatColor'), + setGlobalDefaultConversationColor: action( + 'setGlobalDefaultConversationColor' + ), + } satisfies PropsType, +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const _Preferences = Template.bind({}); -_Preferences.args = getDefaultArgs(); export const Blocked1 = Template.bind({}); Blocked1.args = { @@ -198,9 +197,6 @@ export const CustomUniversalExpireTimer = Template.bind({}); CustomUniversalExpireTimer.args = { universalExpireTimer: DurationInSeconds.fromSeconds(9000), }; -CustomUniversalExpireTimer.story = { - name: 'Custom universalExpireTimer', -}; export const PNPSharingDisabled = Template.bind({}); PNPSharingDisabled.args = { @@ -208,9 +204,6 @@ PNPSharingDisabled.args = { whoCanFindMe: PhoneNumberDiscoverability.Discoverable, isPhoneNumberSharingSupported: true, }; -PNPSharingDisabled.story = { - name: 'PNP Sharing Disabled', -}; export const PNPDiscoverabilityDisabled = Template.bind({}); PNPDiscoverabilityDisabled.args = { @@ -218,9 +211,6 @@ PNPDiscoverabilityDisabled.args = { whoCanFindMe: PhoneNumberDiscoverability.NotDiscoverable, isPhoneNumberSharingSupported: true, }; -PNPDiscoverabilityDisabled.story = { - name: 'PNP Discoverability Disabled', -}; export const FormattingDisabled = Template.bind({}); FormattingDisabled.args = { diff --git a/ts/components/ProfileEditor.stories.tsx b/ts/components/ProfileEditor.stories.tsx index 32c388e2dcf1..9fe216309f80 100644 --- a/ts/components/ProfileEditor.stories.tsx +++ b/ts/components/ProfileEditor.stories.tsx @@ -1,7 +1,7 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import React, { useState } from 'react'; import casual from 'casual'; @@ -18,6 +18,7 @@ import { } from '../state/ducks/usernameEnums'; import { getRandomColor } from '../test-both/helpers/getRandomColor'; import { setupI18n } from '../util/setupI18n'; +import { SignalService as Proto } from '../protobuf'; const i18n = setupI18n('en', enMessages); @@ -25,44 +26,11 @@ export default { component: ProfileEditor, title: 'Components/ProfileEditor', argTypes: { - aboutEmoji: { - defaultValue: '', - }, - aboutText: { - defaultValue: casual.sentence, - }, - profileAvatarPath: { - defaultValue: undefined, - }, - conversationId: { - defaultValue: generateUuid(), - }, - color: { - defaultValue: getRandomColor(), - }, - deleteAvatarFromDisk: { action: true }, - familyName: { - defaultValue: casual.last_name, - }, - firstName: { - defaultValue: casual.first_name, - }, - i18n: { - defaultValue: i18n, - }, - usernameLink: { - defaultValue: 'https://signal.me/#eu/testtest', - }, - usernameLinkFgColor: { - defaultValue: '', - }, isUsernameFlagEnabled: { control: { type: 'checkbox' }, - defaultValue: false, }, usernameEditState: { control: { type: 'radio' }, - defaultValue: UsernameEditState.Editing, options: { Editing: UsernameEditState.Editing, ConfirmingDelete: UsernameEditState.ConfirmingDelete, @@ -71,37 +39,49 @@ export default { }, usernameLinkState: { control: { type: 'select' }, - defaultValue: UsernameLinkState.Ready, options: [UsernameLinkState.Ready, UsernameLinkState.Updating], }, - onEditStateChanged: { action: true }, - onProfileChanged: { action: true }, - onSetSkinTone: { action: true }, - saveAttachment: { action: true }, - setUsernameLinkColor: { action: true }, - showToast: { action: true }, - recentEmojis: { - defaultValue: [], - }, - replaceAvatar: { action: true }, - resetUsernameLink: { action: true }, - saveAvatarToDisk: { action: true }, - markCompletedUsernameOnboarding: { action: true }, - markCompletedUsernameLinkOnboarding: { action: true }, - openUsernameReservationModal: { action: true }, - setUsernameEditState: { action: true }, - deleteUsername: { action: true }, - skinTone: { - defaultValue: 0, - }, - userAvatarData: { - defaultValue: [], - }, - username: { - defaultValue: undefined, - }, }, -} as Meta; + args: { + aboutEmoji: '', + aboutText: casual.sentence, + profileAvatarPath: undefined, + conversationId: generateUuid(), + color: getRandomColor(), + deleteAvatarFromDisk: action('deleteAvatarFromDisk'), + familyName: casual.last_name, + firstName: casual.first_name, + i18n, + + usernameLink: 'https://signal.me/#eu/testtest', + usernameLinkColor: Proto.AccountRecord.UsernameLink.Color.PURPLE, + isUsernameFlagEnabled: false, + usernameEditState: UsernameEditState.Editing, + usernameLinkState: UsernameLinkState.Ready, + + recentEmojis: [], + skinTone: 0, + userAvatarData: [], + username: undefined, + + onEditStateChanged: action('onEditStateChanged'), + onProfileChanged: action('onProfileChanged'), + onSetSkinTone: action('onSetSkinTone'), + saveAttachment: action('saveAttachment'), + setUsernameLinkColor: action('setUsernameLinkColor'), + showToast: action('showToast'), + replaceAvatar: action('replaceAvatar'), + resetUsernameLink: action('resetUsernameLink'), + saveAvatarToDisk: action('saveAvatarToDisk'), + markCompletedUsernameOnboarding: action('markCompletedUsernameOnboarding'), + markCompletedUsernameLinkOnboarding: action( + 'markCompletedUsernameLinkOnboarding' + ), + openUsernameReservationModal: action('openUsernameReservationModal'), + setUsernameEditState: action('setUsernameEditState'), + deleteUsername: action('deleteUsername'), + }, +} satisfies Meta; function renderEditUsernameModalBody(props: { onClose: () => void; @@ -122,7 +102,7 @@ function renderEditUsernameModalBody(props: { } // eslint-disable-next-line react/function-component-definition -const Template: Story = args => { +const Template: StoryFn = args => { const [skinTone, setSkinTone] = useState(0); return ( @@ -148,35 +128,22 @@ export const WithFullName = Template.bind({}); WithFullName.args = { familyName: casual.last_name, }; -WithFullName.story = { - name: 'with Full Name', -}; - export const WithCustomAbout = Template.bind({}); WithCustomAbout.args = { aboutEmoji: '🙏', aboutText: 'Live. Laugh. Love', }; -WithCustomAbout.story = { - name: 'with Custom About', -}; export const WithUsernameFlagEnabled = Template.bind({}); WithUsernameFlagEnabled.args = { isUsernameFlagEnabled: true, }; -WithUsernameFlagEnabled.story = { - name: 'with Username flag enabled', -}; export const WithUsernameFlagEnabledAndUsername = Template.bind({}); WithUsernameFlagEnabledAndUsername.args = { isUsernameFlagEnabled: true, username: 'signaluser.123', }; -WithUsernameFlagEnabledAndUsername.story = { - name: 'with Username flag enabled and username', -}; export const DeletingUsername = Template.bind({}); DeletingUsername.args = { diff --git a/ts/components/ProgressDialog.stories.tsx b/ts/components/ProgressDialog.stories.tsx index 88b5ced0a89a..a4ebc518474b 100644 --- a/ts/components/ProgressDialog.stories.tsx +++ b/ts/components/ProgressDialog.stories.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './ProgressDialog'; import { ProgressDialog } from './ProgressDialog'; import { setupI18n } from '../util/setupI18n'; @@ -11,7 +12,7 @@ import enMessages from '../../_locales/en/messages.json'; export default { title: 'Components/ProgressDialog', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); diff --git a/ts/components/ProgressModal.stories.tsx b/ts/components/ProgressModal.stories.tsx index ae82f83a0035..3a770d717cd7 100644 --- a/ts/components/ProgressModal.stories.tsx +++ b/ts/components/ProgressModal.stories.tsx @@ -2,9 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ProgressModal'; import { ProgressModal } from './ProgressModal'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -12,7 +12,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/ProgressModal', -}; +} satisfies Meta; export function Normal(): JSX.Element { return ; diff --git a/ts/components/QrCode.stories.tsx b/ts/components/QrCode.stories.tsx index 380f7d09ab69..6e91594591dc 100644 --- a/ts/components/QrCode.stories.tsx +++ b/ts/components/QrCode.stories.tsx @@ -2,12 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './QrCode'; import { QrCode } from './QrCode'; export default { title: 'Components/QrCode', -}; +} satisfies Meta; export function Default(): JSX.Element { return ( diff --git a/ts/components/QrCode.tsx b/ts/components/QrCode.tsx index 416edb50e050..1d865ec99c8d 100644 --- a/ts/components/QrCode.tsx +++ b/ts/components/QrCode.tsx @@ -9,7 +9,7 @@ import { getEnvironment, Environment } from '../environment'; const AUTODETECT_TYPE_NUMBER = 0; const ERROR_CORRECTION_LEVEL = 'L'; -type PropsType = Readonly<{ +export type PropsType = Readonly<{ alt: string; className?: string; data: string | Uint8Array; diff --git a/ts/components/SafetyNumberChangeDialog.stories.tsx b/ts/components/SafetyNumberChangeDialog.stories.tsx index b9f5a3ce3620..394c960ddbd7 100644 --- a/ts/components/SafetyNumberChangeDialog.stories.tsx +++ b/ts/components/SafetyNumberChangeDialog.stories.tsx @@ -3,7 +3,8 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { Props } from './SafetyNumberChangeDialog'; import { SafetyNumberChangeDialog } from './SafetyNumberChangeDialog'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { setupI18n } from '../util/setupI18n'; @@ -57,7 +58,7 @@ const useTheme = () => React.useContext(StorybookThemeContext); export default { title: 'Components/SafetyNumberChangeDialog', -}; +} satisfies Meta; export function SingleContactDialog(): JSX.Element { const theme = useTheme(); @@ -162,9 +163,6 @@ export function AllVerified(): JSX.Element { /> ); } -AllVerified.story = { - name: 'All verified; Send button instead', -}; export function MultipleContactsAllWithBadges(): JSX.Element { const theme = useTheme(); @@ -195,10 +193,6 @@ export function MultipleContactsAllWithBadges(): JSX.Element { ); } -MultipleContactsAllWithBadges.story = { - name: 'Multiple contacts, all with badges', -}; - export function TenContacts(): JSX.Element { const theme = useTheme(); return ( @@ -234,10 +228,6 @@ export function TenContacts(): JSX.Element { ); } -TenContacts.story = { - name: 'Ten contacts; first isReviewing = false, then scrolling dialog', -}; - export function NoContacts(): JSX.Element { const theme = useTheme(); return ( diff --git a/ts/components/SafetyNumberNotReady.stories.tsx b/ts/components/SafetyNumberNotReady.stories.tsx index 108d1c0b7d12..dc3f3e51b58f 100644 --- a/ts/components/SafetyNumberNotReady.stories.tsx +++ b/ts/components/SafetyNumberNotReady.stories.tsx @@ -3,7 +3,8 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './SafetyNumberNotReady'; import { SafetyNumberNotReady } from './SafetyNumberNotReady'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -12,12 +13,8 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/SafetyNumberNotReady', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; } - -Default.story = { - name: 'Safety Number Not Ready', -}; diff --git a/ts/components/SafetyNumberOnboarding.stories.tsx b/ts/components/SafetyNumberOnboarding.stories.tsx index fd062bf0abf7..0d81fb3535aa 100644 --- a/ts/components/SafetyNumberOnboarding.stories.tsx +++ b/ts/components/SafetyNumberOnboarding.stories.tsx @@ -3,7 +3,8 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './SafetyNumberOnboarding'; import { SafetyNumberOnboarding } from './SafetyNumberOnboarding'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -12,12 +13,8 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/SafetyNumberOnboarding', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; } - -Default.story = { - name: 'Safety Number Onboarding', -}; diff --git a/ts/components/SafetyNumberViewer.stories.tsx b/ts/components/SafetyNumberViewer.stories.tsx index eb4766466edd..e03c06722c44 100644 --- a/ts/components/SafetyNumberViewer.stories.tsx +++ b/ts/components/SafetyNumberViewer.stories.tsx @@ -3,8 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; -import { boolean, text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './SafetyNumberViewer'; import { SafetyNumberViewer } from './SafetyNumberViewer'; import { setupI18n } from '../util/setupI18n'; @@ -78,26 +77,21 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ safetyNumbers: overrideProps.safetyNumbers ?? [ { identifierType: SafetyNumberIdentifierType.ACIIdentifier, - numberBlocks: text( - 'safetyNumber', - generateNumberBlocks().join(' ') - ).split(' '), + numberBlocks: generateNumberBlocks(), qrData: generateQRData(), }, ], toggleVerified: action('toggle-verified'), - verificationDisabled: boolean( - 'verificationDisabled', + verificationDisabled: overrideProps.verificationDisabled !== undefined ? overrideProps.verificationDisabled - : false - ), + : false, onClose: action('onClose'), }); export default { title: 'Components/SafetyNumberViewer', -}; +} satisfies Meta; export function SafetyNumber(): JSX.Element { return ; @@ -111,10 +105,7 @@ export function SafetyNumberBeforeE164Transition(): JSX.Element { safetyNumbers: [ { identifierType: SafetyNumberIdentifierType.E164Identifier, - numberBlocks: text( - 'safetyNumber', - generateNumberBlocks().join(' ') - ).split(' '), + numberBlocks: generateNumberBlocks(), qrData: generateQRData(), }, ], @@ -123,10 +114,6 @@ export function SafetyNumberBeforeE164Transition(): JSX.Element { ); } -SafetyNumberBeforeE164Transition.story = { - name: 'Safety Number (before e164 transition)', -}; - export function SafetyNumberE164Transition(): JSX.Element { return ( ); } - -NoPhoneNumberCannotVerify.story = { - name: 'No Phone Number (cannot verify)', -}; diff --git a/ts/components/Select.stories.tsx b/ts/components/Select.stories.tsx index 82b938b15b82..d137fdd71597 100644 --- a/ts/components/Select.stories.tsx +++ b/ts/components/Select.stories.tsx @@ -3,12 +3,13 @@ import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './Select'; import { Select } from './Select'; export default { title: 'Components/Select', -}; +} satisfies Meta; export function Normal(): JSX.Element { const [value, setValue] = useState(0); @@ -45,7 +46,3 @@ export function WithDisabledOptions(): JSX.Element { /> ); } - -WithDisabledOptions.story = { - name: 'With disabled options', -}; diff --git a/ts/components/SendStoryModal.stories.tsx b/ts/components/SendStoryModal.stories.tsx index dd87801a7920..4d9a4f0dfc46 100644 --- a/ts/components/SendStoryModal.stories.tsx +++ b/ts/components/SendStoryModal.stories.tsx @@ -1,9 +1,10 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './SendStoryModal'; import enMessages from '../../_locales/en/messages.json'; import { SendStoryModal } from './SendStoryModal'; @@ -17,6 +18,7 @@ import { getFakeDistributionListsWithMembers, } from '../test-both/helpers/getFakeDistributionLists'; import { VIDEO_MP4 } from '../types/MIME'; +import type { StoryDistributionIdString } from '../types/StoryDistributionId'; const i18n = setupI18n('en', enMessages); @@ -28,99 +30,104 @@ const myStories = { export default { title: 'Components/SendStoryModal', component: SendStoryModal, - argTypes: { + args: { draftAttachment: { - defaultValue: { - contentType: VIDEO_MP4, - fileName: 'pixabay-Soap-Bubble-7141.mp4', - url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', - }, + contentType: VIDEO_MP4, + fileName: 'pixabay-Soap-Bubble-7141.mp4', + url: '/fixtures/pixabay-Soap-Bubble-7141.mp4', + size: 1, }, - candidateConversations: { - defaultValue: Array.from(Array(100), () => getDefaultConversation()), - }, - distributionLists: { - defaultValue: [myStories], - }, - getPreferredBadge: { action: true }, - groupConversations: { - defaultValue: Array.from(Array(7), getDefaultGroup), - }, - groupStories: { - defaultValue: Array.from(Array(2), getDefaultGroup), - }, - hasFirstStoryPostExperience: { - defaultValue: false, - }, - i18n: { - defaultValue: i18n, - }, - me: { - defaultValue: getDefaultConversation(), - }, - onClose: { action: true }, - onDeleteList: { action: true }, - onDistributionListCreated: { action: true }, - onHideMyStoriesFrom: { action: true }, - onMediaPlaybackStart: { action: true }, - onSend: { action: true }, - onViewersUpdated: { action: true }, - setMyStoriesToAllSignalConnections: { action: true }, - mostRecentActiveStoryTimestampByGroupOrDistributionList: { - defaultValue: {}, - }, - signalConnections: { - defaultValue: Array.from(Array(42), getDefaultConversation), - }, - toggleGroupsForStorySend: { action: true }, - toggleSignalConnectionsModal: { action: true }, + candidateConversations: Array.from(Array(100), () => + getDefaultConversation() + ), + distributionLists: [myStories], + getPreferredBadge: () => undefined, + groupConversations: Array.from(Array(7), getDefaultGroup), + groupStories: Array.from(Array(2), getDefaultGroup), + hasFirstStoryPostExperience: false, + i18n, + me: getDefaultConversation(), + onClose: action('onClose'), + onDeleteList: action('onDeleteList'), + onDistributionListCreated: () => + Promise.resolve('' as StoryDistributionIdString), + onHideMyStoriesFrom: action('onHideMyStoriesFrom'), + onMediaPlaybackStart: action('onMediaPlaybackStart'), + onSend: action('onSend'), + onViewersUpdated: action('onViewersUpdated'), + setMyStoriesToAllSignalConnections: action( + 'setMyStoriesToAllSignalConnections' + ), + mostRecentActiveStoryTimestampByGroupOrDistributionList: {}, + signalConnections: Array.from(Array(42), getDefaultConversation), + toggleGroupsForStorySend: () => Promise.resolve(), + toggleSignalConnectionsModal: action('toggleSignalConnectionsModal'), }, -} as Meta; +} satisfies Meta; -// eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +export function Modal(args: PropsType): JSX.Element { + return ( + + ); +} -export const Modal = Template.bind({}); -Modal.args = { - distributionLists: getFakeDistributionListsWithMembers(), -}; +export function BlockList(args: PropsType): JSX.Element { + return ( + + ); +} -export const BlockList = Template.bind({}); -BlockList.args = { - distributionLists: [ - { ...getMyStories(), members: [getDefaultConversation()] }, - ], - groupStories: [], -}; +export function AllowList(args: PropsType): JSX.Element { + return ( + + ); +} -export const AllowList = Template.bind({}); -AllowList.args = { - distributionLists: [ - { - ...getMyStories(), - isBlockList: false, - members: [getDefaultConversation()], - }, - ], - groupStories: [], -}; +export function FirstTime(args: PropsType): JSX.Element { + return ( + + ); +} -export const FirstTime = Template.bind({}); -FirstTime.args = { - distributionLists: [myStories], - groupStories: [], - hasFirstStoryPostExperience: true, -}; - -export const FirstTimeAlreadyConfiguredOnMobile = Template.bind({}); -FirstTime.args = { - distributionLists: [ - { - ...myStories, - isBlockList: false, - members: Array.from(Array(3), getDefaultConversation), - }, - ], - groupStories: [], - hasFirstStoryPostExperience: true, -}; +export function FirstTimeAlreadyConfiguredOnMobile( + args: PropsType +): JSX.Element { + return ( + + ); +} diff --git a/ts/components/ShortcutGuide.stories.tsx b/ts/components/ShortcutGuide.stories.tsx index 6766b930b13a..7520b62d31c2 100644 --- a/ts/components/ShortcutGuide.stories.tsx +++ b/ts/components/ShortcutGuide.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; import type { Props } from './ShortcutGuide'; @@ -13,7 +13,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/ShortcutGuide', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ i18n, diff --git a/ts/components/SignalConnectionsModal.stories.tsx b/ts/components/SignalConnectionsModal.stories.tsx index dec509bb5dcf..806031622139 100644 --- a/ts/components/SignalConnectionsModal.stories.tsx +++ b/ts/components/SignalConnectionsModal.stories.tsx @@ -1,9 +1,10 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './SignalConnectionsModal'; import enMessages from '../../_locales/en/messages.json'; import { SignalConnectionsModal } from './SignalConnectionsModal'; @@ -14,16 +15,16 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/SignalConnectionsModal', component: SignalConnectionsModal, - argTypes: { - i18n: { - defaultValue: i18n, - }, - onClose: { action: true }, + args: { + i18n, + onClose: action('onClose'), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ( + +); export const Modal = Template.bind({}); Modal.args = {}; diff --git a/ts/components/Slider.stories.tsx b/ts/components/Slider.stories.tsx index 897641df0bda..c998aae130fc 100644 --- a/ts/components/Slider.stories.tsx +++ b/ts/components/Slider.stories.tsx @@ -5,12 +5,13 @@ import React, { useState } from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './Slider'; import { Slider } from './Slider'; export default { title: 'Components/Slider', -}; +} satisfies Meta; const createProps = (): PropsType => ({ label: 'Slider Handle', diff --git a/ts/components/Spinner.stories.tsx b/ts/components/Spinner.stories.tsx index bd73aa4e753c..eeda19876575 100644 --- a/ts/components/Spinner.stories.tsx +++ b/ts/components/Spinner.stories.tsx @@ -2,58 +2,43 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - -import { select, text } from '@storybook/addon-knobs'; +import type { Meta } from '@storybook/react'; import type { Props } from './Spinner'; import { Spinner, SpinnerDirections, SpinnerSvgSizes } from './Spinner'; export default { title: 'Components/Spinner', -}; + argTypes: { + size: { control: { type: 'text' } }, + svgSize: { control: { type: 'select' }, options: SpinnerSvgSizes }, + direction: { control: { type: 'select' }, options: SpinnerDirections }, + }, + args: { + size: '20px', + svgSize: 'normal', + direction: undefined, + }, +} satisfies Meta; -const createProps = (overrideProps: Partial = {}): Props => ({ - size: text('size', overrideProps.size || ''), - svgSize: select( - 'svgSize', - SpinnerSvgSizes.reduce((m, s) => ({ ...m, [s]: s }), {}), - overrideProps.svgSize || 'normal' - ), - direction: select( - 'direction', - SpinnerDirections.reduce((d, s) => ({ ...d, [s]: s }), {}), - overrideProps.direction - ), -}); - -export function Normal(): JSX.Element { - const props = createProps(); - - return ; +export function Normal(args: Props): JSX.Element { + return ; } -export function SvgSizes(): JSX.Element { - const props = createProps(); - +export function SvgSizes(args: Props): JSX.Element { return ( <> {SpinnerSvgSizes.map(svgSize => ( - + ))} ); } -SvgSizes.story = { - name: 'SVG Sizes', -}; - -export function Directions(): JSX.Element { - const props = createProps(); - +export function Directions(args: Props): JSX.Element { return ( <> {SpinnerDirections.map(direction => ( - + ))} ); diff --git a/ts/components/StoriesSettingsModal.stories.tsx b/ts/components/StoriesSettingsModal.stories.tsx index 7343197e1d37..de1848c85e76 100644 --- a/ts/components/StoriesSettingsModal.stories.tsx +++ b/ts/components/StoriesSettingsModal.stories.tsx @@ -1,9 +1,10 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './StoriesSettingsModal'; import enMessages from '../../_locales/en/messages.json'; import { StoriesSettingsModal } from './StoriesSettingsModal'; @@ -23,45 +24,37 @@ export default { title: 'Components/StoriesSettingsModal', component: StoriesSettingsModal, argTypes: { - candidateConversations: { - defaultValue: Array.from(Array(100), () => getDefaultConversation()), - }, - signalConnections: { - defaultValue: Array.from(Array(42), getDefaultConversation), - }, - distributionLists: { - defaultValue: [], - }, - groupStories: { - defaultValue: Array.from(Array(2), getDefaultGroup), - }, - getPreferredBadge: { action: true }, - hideStoriesSettings: { action: true }, - i18n: { - defaultValue: i18n, - }, - me: { - defaultValue: getDefaultConversation(), - }, storyViewReceiptsEnabled: { control: 'boolean' }, - onDeleteList: { action: true }, - toggleGroupsForStorySend: { action: true }, - onDistributionListCreated: { action: true }, - onHideMyStoriesFrom: { action: true }, - onRemoveMembers: { action: true }, - onRepliesNReactionsChanged: { action: true }, - onViewersUpdated: { action: true }, - setMyStoriesToAllSignalConnections: { action: true }, - toggleSignalConnectionsModal: { action: true }, - setStoriesDisabled: { action: true }, - getConversationByServiceId: { - defaultValue: () => getDefaultGroup(), - }, }, -} as Meta; + args: { + candidateConversations: Array.from(Array(100), () => + getDefaultConversation() + ), + signalConnections: Array.from(Array(42), getDefaultConversation), + distributionLists: [], + groupStories: Array.from(Array(2), getDefaultGroup), + getPreferredBadge: () => undefined, + hideStoriesSettings: action('hideStoriesSettings'), + i18n, + me: getDefaultConversation(), + onDeleteList: action('onDeleteList'), + toggleGroupsForStorySend: action('toggleGroupsForStorySend'), + onDistributionListCreated: () => Promise.resolve(''), + onHideMyStoriesFrom: action('onHideMyStoriesFrom'), + onRemoveMembers: action('onRemoveMembers'), + onRepliesNReactionsChanged: action('onRepliesNReactionsChanged'), + onViewersUpdated: action('onViewersUpdated'), + setMyStoriesToAllSignalConnections: action( + 'setMyStoriesToAllSignalConnections' + ), + toggleSignalConnectionsModal: action('toggleSignalConnectionsModal'), + setStoriesDisabled: action('setStoriesDisabled'), + getConversationByServiceId: () => getDefaultGroup(), + }, +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const MyStories = Template.bind({}); { diff --git a/ts/components/StoriesTab.stories.tsx b/ts/components/StoriesTab.stories.tsx index acb2beffec69..9a3f37a15569 100644 --- a/ts/components/StoriesTab.stories.tsx +++ b/ts/components/StoriesTab.stories.tsx @@ -1,9 +1,10 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './StoriesTab'; import { StoriesTab } from './StoriesTab'; import enMessages from '../../_locales/en/messages.json'; @@ -20,50 +21,32 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/StoriesTab', component: StoriesTab, - argTypes: { - deleteStoryForEveryone: { action: true }, - getPreferredBadge: { action: true }, - hiddenStories: { - defaultValue: [], - }, - i18n: { - defaultValue: i18n, - }, - maxAttachmentSizeInKb: { - defaultValue: 100 * 1024, - }, - me: { - defaultValue: getDefaultConversation(), - }, - myStories: { - defaultValue: [], - }, - onForwardStory: { action: true }, - onSaveStory: { action: true }, - ourConversationId: { - defaultValue: getDefaultConversation().id, - }, - preferredWidthFromStorage: { - defaultValue: 380, - }, - queueStoryDownload: { action: true }, - renderStoryCreator: { action: true }, - retryMessageSend: { action: true }, - showConversation: { action: true }, - showStoriesSettings: { action: true }, - showToast: { action: true }, - stories: { - defaultValue: [], - }, - toggleHideStories: { action: true }, - toggleStoriesView: { action: true }, - viewUserStories: { action: true }, - viewStory: { action: true }, + args: { + deleteStoryForEveryone: action('deleteStoryForEveryone'), + getPreferredBadge: () => undefined, + hiddenStories: [], + i18n, + maxAttachmentSizeInKb: 100 * 1024, + me: getDefaultConversation(), + myStories: [], + onForwardStory: action('onForwardStory'), + onSaveStory: action('onSaveStory'), + preferredWidthFromStorage: 380, + queueStoryDownload: action('queueStoryDownload'), + renderStoryCreator: () => <>StoryCreator, + retryMessageSend: action('retryMessageSend'), + showConversation: action('showConversation'), + showStoriesSettings: action('showStoriesSettings'), + showToast: action('showToast'), + stories: [], + toggleHideStories: action('toggleHideStories'), + viewUserStories: action('viewUserStories'), + viewStory: action('viewStory'), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const Blank = Template.bind({}); Blank.args = {}; diff --git a/ts/components/StoryCreator.stories.tsx b/ts/components/StoryCreator.stories.tsx index 35f35aaa2bac..ff1f7720f3de 100644 --- a/ts/components/StoryCreator.stories.tsx +++ b/ts/components/StoryCreator.stories.tsx @@ -1,9 +1,10 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './StoryCreator'; import enMessages from '../../_locales/en/messages.json'; import { StoryCreator } from './StoryCreator'; @@ -20,71 +21,46 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/StoryCreator', component: StoryCreator, - argTypes: { - candidateConversations: { - defaultValue: Array.from(Array(100), getDefaultConversation), - }, - debouncedMaybeGrabLinkPreview: { action: true }, - distributionLists: { defaultValue: getFakeDistributionListsWithMembers() }, - getPreferredBadge: { action: true }, - groupConversations: { - defaultValue: Array.from(Array(7), getDefaultGroup), - }, - groupStories: { - defaultValue: Array.from(Array(4), getDefaultGroup), - }, - hasFirstStoryPostExperience: { - defaultValue: false, - }, - i18n: { defaultValue: i18n }, + args: { + candidateConversations: Array.from(Array(100), getDefaultConversation), + debouncedMaybeGrabLinkPreview: action('debouncedMaybeGrabLinkPreview'), + distributionLists: getFakeDistributionListsWithMembers(), + getPreferredBadge: () => undefined, + groupConversations: Array.from(Array(7), getDefaultGroup), + groupStories: Array.from(Array(4), getDefaultGroup), + hasFirstStoryPostExperience: false, + i18n, imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]', - installedPacks: { - defaultValue: [], - }, - isSending: { - defaultValue: false, - }, - linkPreview: { - defaultValue: undefined, - }, - me: { - defaultValue: getDefaultConversation(), - }, - onClose: { action: true }, - onDeleteList: { action: true }, - onDistributionListCreated: { action: true }, - onHideMyStoriesFrom: { action: true }, - onSend: { action: true }, - onSetSkinTone: { action: true }, - onUseEmoji: { action: true }, - onViewersUpdated: { action: true }, - processAttachment: { action: true }, - recentEmojis: { - defaultValue: [], - }, - recentStickers: { - defaultValue: [], - }, - sendStoryModalOpenStateChanged: { action: true }, - setMyStoriesToAllSignalConnections: { action: true }, - signalConnections: { - defaultValue: Array.from(Array(42), getDefaultConversation), - }, - skinTone: { - defaultValue: 0, - }, - toggleSignalConnectionsModal: { action: true }, + installedPacks: [], + isSending: false, + linkPreview: undefined, + me: getDefaultConversation(), + onClose: action('onClose'), + onDeleteList: action('onDeleteList'), + onDistributionListCreated: undefined, + onHideMyStoriesFrom: action('onHideMyStoriesFrom'), + onSend: action('onSend'), + onSetSkinTone: action('onSetSkinTone'), + onUseEmoji: action('onUseEmoji'), + onViewersUpdated: action('onViewersUpdated'), + processAttachment: undefined, + recentEmojis: [], + recentStickers: [], + sendStoryModalOpenStateChanged: action('sendStoryModalOpenStateChanged'), + setMyStoriesToAllSignalConnections: action( + 'setMyStoriesToAllSignalConnections' + ), + signalConnections: Array.from(Array(42), getDefaultConversation), + skinTone: 0, + toggleSignalConnectionsModal: action('toggleSignalConnectionsModal'), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const Default = Template.bind({}); Default.args = {}; -Default.story = { - name: 'w/o Link Preview available', -}; export const LinkPreview = Template.bind({}); LinkPreview.args = { @@ -97,17 +73,11 @@ LinkPreview.args = { url: 'https://www.catsandkittens.lolcats/kittens/page/1', }, }; -LinkPreview.story = { - name: 'with Link Preview ready to be applied', -}; export const FirstTime = Template.bind({}); FirstTime.args = { hasFirstStoryPostExperience: true, }; -FirstTime.story = { - name: 'First time posting a story', -}; export const Sending = Template.bind({}); Sending.args = { diff --git a/ts/components/StoryDetailsModal.stories.tsx b/ts/components/StoryDetailsModal.stories.tsx index 50b42db17f7b..63b9e7e6ca68 100644 --- a/ts/components/StoryDetailsModal.stories.tsx +++ b/ts/components/StoryDetailsModal.stories.tsx @@ -1,15 +1,15 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; import casual from 'casual'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './StoryDetailsModal'; import enMessages from '../../_locales/en/messages.json'; import { SendStatus } from '../messages/MessageSendState'; import { StoryDetailsModal } from './StoryDetailsModal'; -import { fakeAttachment } from '../test-both/helpers/fakeAttachment'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { setupI18n } from '../util/setupI18n'; @@ -18,29 +18,18 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/StoryDetailsModal', component: StoryDetailsModal, - argTypes: { - getPreferredBadge: { action: true }, - i18n: { - defaultValue: i18n, - }, - onClose: { action: true }, - sender: { - defaultValue: getDefaultConversation(), - }, - sendState: { - defaultValue: undefined, - }, - size: { - defaultValue: fakeAttachment().size, - }, - timestamp: { - defaultValue: Date.now(), - }, + args: { + getPreferredBadge: () => undefined, + i18n, + onClose: action('onClose'), + sender: getDefaultConversation(), + sendState: undefined, + timestamp: Date.now(), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const MyStory = Template.bind({}); MyStory.args = { diff --git a/ts/components/StoryImage.stories.tsx b/ts/components/StoryImage.stories.tsx index e50c6b47cdd4..909a4fe5363c 100644 --- a/ts/components/StoryImage.stories.tsx +++ b/ts/components/StoryImage.stories.tsx @@ -6,6 +6,7 @@ import { v4 as uuid } from 'uuid'; import { action } from '@storybook/addon-actions'; import { noop } from 'lodash'; +import type { Meta } from '@storybook/react'; import type { PropsType } from './StoryImage'; import { StoryImage } from './StoryImage'; import enMessages from '../../_locales/en/messages.json'; @@ -20,7 +21,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/StoryImage', -}; +} satisfies Meta; function getDefaultProps(): PropsType { return { @@ -41,26 +42,14 @@ export function GoodStory(): JSX.Element { return ; } -GoodStory.story = { - name: 'Good story', -}; - export function GoodStoryThumbnail(): JSX.Element { return ; } -GoodStoryThumbnail.story = { - name: 'Good story (thumbnail)', -}; - export function NotDownloaded(): JSX.Element { return ; } -NotDownloaded.story = { - name: 'Not downloaded', -}; - export function NotDownloadedThumbnail(): JSX.Element { return ( { + log.error( + 'StoryImage: Failed to play video', + Errors.toLogFormat(error) + ); + }); } }, [isPaused, onMediaPlaybackStart]); diff --git a/ts/components/StoryLinkPreview.stories.tsx b/ts/components/StoryLinkPreview.stories.tsx index 8bffcea296ed..b46ba21e41a0 100644 --- a/ts/components/StoryLinkPreview.stories.tsx +++ b/ts/components/StoryLinkPreview.stories.tsx @@ -1,7 +1,7 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; import type { Props } from './StoryLinkPreview'; @@ -21,35 +21,23 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/StoryLinkPreview', component: StoryLinkPreview, - argTypes: { - description: { - defaultValue: - 'Introducing Mac Studio. Stunningly compact. Endless connectivity. And astonishing performance with M1 Max or the new M1 Ultra chip.', - }, - forceCompactMode: { - defaultValue: false, - }, - i18n: { - defaultValue: i18n, - }, - image: { - defaultValue: fakeAttachment({ - // url: 'https://www.apple.com/v/mac-studio/c/images/meta/mac-studio_overview__eedzbosm1t26_og.png', - url: '/fixtures/kitten-4-112-112.jpg', - contentType: IMAGE_JPEG, - }), - }, - title: { - defaultValue: 'Mac Studio', - }, - url: { - defaultValue: 'https://www.apple.com/mac-studio/', - }, + args: { + description: + 'Introducing Mac Studio. Stunningly compact. Endless connectivity. And astonishing performance with M1 Max or the new M1 Ultra chip.', + forceCompactMode: false, + i18n, + image: fakeAttachment({ + // url: 'https://www.apple.com/v/mac-studio/c/images/meta/mac-studio_overview__eedzbosm1t26_og.png', + url: '/fixtures/kitten-4-112-112.jpg', + contentType: IMAGE_JPEG, + }), + title: 'Mac Studio', + url: 'https://www.apple.com/mac-studio/', }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const Default = Template.bind({}); diff --git a/ts/components/StoryListItem.stories.tsx b/ts/components/StoryListItem.stories.tsx index 14c85c49cb8b..4a2f0d97b47f 100644 --- a/ts/components/StoryListItem.stories.tsx +++ b/ts/components/StoryListItem.stories.tsx @@ -1,9 +1,10 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './StoryListItem'; import { StoryListItem } from './StoryListItem'; import enMessages from '../../_locales/en/messages.json'; @@ -19,30 +20,26 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/StoryListItem', component: StoryListItem, - argTypes: { - conversationId: { - defaultValue: getDefaultConversation().id, - }, - getPreferredBadge: { action: true }, - i18n: { - defaultValue: i18n, - }, - onGoToConversation: { action: true }, - onHideStory: { action: true }, - queueStoryDownload: { action: true }, + args: { + conversationId: getDefaultConversation().id, + getPreferredBadge: () => undefined, + i18n, + onGoToConversation: action('onGoToConversation'), + onHideStory: action('onHideStory'), + queueStoryDownload: action('queueStoryDownload'), story: { - defaultValue: { - messageId: '123', - sender: getDefaultConversation(), - timestamp: Date.now(), - }, + messageId: '123', + sender: getDefaultConversation(), + timestamp: Date.now(), + messageIdForLogging: '123', + expirationTimestamp: undefined, }, - viewUserStories: { action: true }, + viewUserStories: action('viewUserStories'), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const SomeonesStory = Template.bind({}); SomeonesStory.args = { @@ -60,6 +57,3 @@ SomeonesStory.args = { expirationTimestamp: undefined, }, }; -SomeonesStory.story = { - name: "Someone's story", -}; diff --git a/ts/components/StoryViewer.stories.tsx b/ts/components/StoryViewer.stories.tsx index 0c21442de86b..1605b7e96ac3 100644 --- a/ts/components/StoryViewer.stories.tsx +++ b/ts/components/StoryViewer.stories.tsx @@ -1,9 +1,9 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta } from '@storybook/react'; import React from 'react'; - +import { action } from '@storybook/addon-actions'; import type { PropsType } from './StoryViewer'; import enMessages from '../../_locales/en/messages.json'; import { SendStatus } from '../messages/MessageSendState'; @@ -22,244 +22,237 @@ export default { title: 'Components/StoryViewer', component: StoryViewer, argTypes: { - currentIndex: { - defaultvalue: 0, - }, - getPreferredBadge: { action: true }, - group: { - defaultValue: undefined, - }, - hasAllStoriesMuted: { + hasAllStoriesUnmuted: { control: 'boolean', - defaultValue: false, }, hasViewReceiptSetting: { control: 'boolean', - defaultValue: true, }, - i18n: { - defaultValue: i18n, - }, - platform: { - defaultValue: 'darwin', - }, - loadStoryReplies: { action: true }, - markStoryRead: { action: true }, - numStories: { - defaultValue: 1, - }, - onGoToConversation: { action: true }, - onHideStory: { action: true }, - onReactToStory: { action: true }, - onReplyToStory: { action: true }, - onSetSkinTone: { action: true }, - onTextTooLong: { action: true }, - onUseEmoji: { action: true }, - preferredReactionEmoji: { - defaultValue: ['❤️', '👍', '👎', '😂', '😮', '😢'], - }, - queueStoryDownload: { action: true }, - renderEmojiPicker: { action: true }, - retryMessageSend: { action: true }, - showToast: { action: true }, - skinTone: { - defaultValue: 0, - }, - story: { - defaultValue: getFakeStoryView(), - }, - storyViewMode: { - defaultValue: StoryViewModeType.All, - }, - toggleHasAllStoriesMuted: { action: true }, - viewStory: { action: true }, - isWindowActive: { defaultValue: true }, }, args: { currentIndex: 0, + getPreferredBadge: () => undefined, + group: undefined, + hasAllStoriesUnmuted: true, + hasViewReceiptSetting: true, + i18n, + platform: 'darwin', + loadStoryReplies: action('loadStoryReplies'), + markStoryRead: action('markStoryRead'), + numStories: 1, + onGoToConversation: action('onGoToConversation'), + onHideStory: action('onHideStory'), + onReactToStory: action('onReactToStory'), + onReplyToStory: action('onReplyToStory'), + onSetSkinTone: action('onSetSkinTone'), + onTextTooLong: action('onTextTooLong'), + onUseEmoji: action('onUseEmoji'), + onMediaPlaybackStart: action('onMediaPlaybackStart'), + preferredReactionEmoji: ['❤️', '👍', '👎', '😂', '😮', '😢'], + queueStoryDownload: action('queueStoryDownload'), + renderEmojiPicker: () => <>EmojiPicker, + retryMessageSend: action('retryMessageSend'), + showToast: action('showToast'), + skinTone: 0, + story: getFakeStoryView(), + storyViewMode: StoryViewModeType.All, + viewStory: action('viewStory'), + isWindowActive: true, }, -} as Meta; +} satisfies Meta; -// eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +export function SomeonesStory(args: PropsType): JSX.Element { + return ; +} -export const SomeonesStory = Template.bind({}); -SomeonesStory.args = {}; -SomeonesStory.story = { - name: "Someone's story", -}; +export function WideStory(args: PropsType): JSX.Element { + return ( + + ); +} -export const WideStory = Template.bind({}); -WideStory.args = { - story: getFakeStoryView('/fixtures/nathan-anderson-316188-unsplash.jpg'), -}; -WideStory.story = { - name: 'Wide story', -}; +export function InAGroup(args: PropsType): JSX.Element { + return ( + + ); +} -export const InAGroup = Template.bind({}); -InAGroup.args = { - group: getDefaultConversation({ - avatarPath: '/fixtures/kitten-4-112-112.jpg', - title: 'Family Group', - type: 'group', - }), -}; -InAGroup.story = { - name: 'In a group', -}; +export function MultiStory(args: PropsType): JSX.Element { + return ( + + ); +} -export const MultiStory = Template.bind({}); -MultiStory.args = { - currentIndex: 2, - numStories: 7, - story: { - ...getFakeStoryView(), - attachment: fakeAttachment({ - contentType: VIDEO_MP4, - fileName: 'pixabay-Soap-Bubble-7141.mp4', - url: '/fixtures/kitten-4-112-112.jpg', - screenshotPath: '/fixtures/kitten-4-112-112.jpg', - }), - }, -}; -MultiStory.story = { - name: 'Multi story', -}; +export function Caption(args: PropsType): JSX.Element { + return ( + + ); +} -export const Caption = Template.bind({}); -Caption.args = { - story: { - ...getFakeStoryView(), - attachment: fakeAttachment({ - caption: 'This place looks lovely', - path: 'file.jpg', - url: '/fixtures/nathan-anderson-316188-unsplash.jpg', - }), - }, -}; +export function EmojiCaption(args: PropsType): JSX.Element { + return ( + + ); +} -export const EmojiCaption = Template.bind({}); -EmojiCaption.args = { - story: { - ...getFakeStoryView(), - attachment: fakeAttachment({ - caption: 'WOOOOOOOOW 🥰', - path: 'file.jpg', - url: '/fixtures/nathan-anderson-316188-unsplash.jpg', - }), - }, -}; +export function LongCaption(args: PropsType): JSX.Element { + return ( + + ); +} -export const LongCaption = Template.bind({}); -LongCaption.args = { - story: { - ...getFakeStoryView(), - attachment: fakeAttachment({ - caption: - 'Snowycle, snowycle, snowycle\nI want to ride my snowycle, snowycle, snowycle\nI want to ride my snowycle\nI want to ride my snow\nI want to ride my snowycle\nI want to ride it where I like\nSnowycle, snowycle, snowycle\nI want to ride my snowycle, snowycle, snowycle\nI want to ride my snowycle\nI want to ride my snow\nI want to ride my snowycle\nI want to ride it where I like\nSnowycle, snowycle, snowycle\nI want to ride my snowycle, snowycle, snowycle\nI want to ride my snowycle\nI want to ride my snow\nI want to ride my snowycle\nI want to ride it where I like', - path: 'file.jpg', - url: '/fixtures/snow.jpg', - }), - }, -}; +export function YourStory(args: PropsType): JSX.Element { + const storyView = getFakeStoryView( + '/fixtures/nathan-anderson-316188-unsplash.jpg' + ); + return ( + + ); +} -export const YourStory = Template.bind({}); -{ +export function YourStoryFailed(args: PropsType): JSX.Element { const storyView = getFakeStoryView( '/fixtures/nathan-anderson-316188-unsplash.jpg' ); - YourStory.args = { - distributionList: { - id: generateStoryDistributionId(), - name: 'Close Friends', - }, - story: { - ...storyView, - sender: { - ...storyView.sender, - isMe: true, - }, - sendState: [ - { - recipient: getDefaultConversation(), - status: SendStatus.Viewed, + return ( + + ); } -export const YourStoryFailed = Template.bind({}); -{ +export function ReadReceiptsOff(args: PropsType): JSX.Element { const storyView = getFakeStoryView( '/fixtures/nathan-anderson-316188-unsplash.jpg' ); - - YourStoryFailed.args = { - distributionList: { - id: generateStoryDistributionId(), - name: 'Close Friends', - }, - story: { - ...storyView, - sender: { - ...storyView.sender, - isMe: true, - }, - sendState: [ - { - recipient: getDefaultConversation(), - status: SendStatus.Viewed, + return ( + ); - ReadReceiptsOff.args = { - hasViewReceiptSetting: false, - story: { - ...storyView, - sender: { - ...storyView.sender, - isMe: true, - }, - sendState: [ - { - recipient: getDefaultConversation(), - status: SendStatus.Viewed, - }, - { - recipient: getDefaultConversation(), - status: SendStatus.Delivered, - }, - { - recipient: getDefaultConversation(), - status: SendStatus.Pending, - }, - ], - }, - }; } -ReadReceiptsOff.storyName = 'Read receipts turned off'; diff --git a/ts/components/StoryViewsNRepliesModal.stories.tsx b/ts/components/StoryViewsNRepliesModal.stories.tsx index 48e77d77a114..c86261be6919 100644 --- a/ts/components/StoryViewsNRepliesModal.stories.tsx +++ b/ts/components/StoryViewsNRepliesModal.stories.tsx @@ -1,11 +1,12 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; import { v4 as generateUuid } from 'uuid'; import { useArgs } from '@storybook/addons'; +import { action } from '@storybook/addon-actions'; import type { PropsType } from './StoryViewsNRepliesModal'; import * as durations from '../util/durations'; import enMessages from '../../_locales/en/messages.json'; @@ -21,51 +22,35 @@ export default { title: 'Components/StoryViewsNRepliesModal', component: StoryViewsNRepliesModal, argTypes: { - authorTitle: { - defaultValue: getDefaultConversation().title, - }, - canReply: { - defaultValue: true, - }, - getPreferredBadge: { action: true }, hasViewReceiptSetting: { control: 'boolean', - defaultValue: true, }, hasViewsCapability: { control: 'boolean', - defaultValue: false, - }, - i18n: { - defaultValue: i18n, - }, - platform: { - defaultValue: 'darwin', - }, - onClose: { action: true }, - onSetSkinTone: { action: true }, - onReact: { action: true }, - onReply: { action: true }, - onTextTooLong: { action: true }, - onUseEmoji: { action: true }, - preferredReactionEmoji: { - defaultValue: ['❤️', '👍', '👎', '😂', '😮', '😢'], - }, - renderEmojiPicker: { action: true }, - replies: { - defaultValue: [], - }, - views: { - defaultValue: [], - }, - viewTarget: { - defaultValue: StoryViewTargetType.Views, - }, - onChangeViewTarget: { - action: true, }, }, -} as Meta; + args: { + authorTitle: getDefaultConversation().title, + canReply: true, + getPreferredBadge: () => undefined, + hasViewReceiptSetting: true, + hasViewsCapability: false, + i18n, + platform: 'darwin', + onClose: action('onClose'), + onSetSkinTone: action('onSetSkinTone'), + onReact: action('onReact'), + onReply: action('onReply'), + onTextTooLong: action('onTextTooLong'), + onUseEmoji: action('onUseEmoji'), + preferredReactionEmoji: ['❤️', '👍', '👎', '😂', '😮', '😢'], + renderEmojiPicker: () => <>EmojiPicker, + replies: [], + views: [], + viewTarget: StoryViewTargetType.Views, + onChangeViewTarget: action('onChangeViewTarget'), + }, +} satisfies Meta; function getViewsAndReplies() { const p1 = getDefaultConversation(); @@ -162,7 +147,7 @@ function getViewsAndReplies() { } // eslint-disable-next-line react/function-component-definition -const Template: Story = args => { +const Template: StoryFn = args => { const [, updateArgs] = useArgs(); function onChangeViewTarget(viewTarget: StoryViewTargetType) { diff --git a/ts/components/TextAttachment.stories.tsx b/ts/components/TextAttachment.stories.tsx index 0a2624406bef..34a967e99957 100644 --- a/ts/components/TextAttachment.stories.tsx +++ b/ts/components/TextAttachment.stories.tsx @@ -3,6 +3,7 @@ import React from 'react'; +import type { Meta } from '@storybook/react'; import enMessages from '../../_locales/en/messages.json'; import { setupI18n } from '../util/setupI18n'; import { TextAttachment } from './TextAttachment'; @@ -17,7 +18,7 @@ const getDefaultProps = (): PropsType => ({ export default { title: 'Components/TextAttachment', -}; +} satisfies Meta; export function SolidBgTextBg(): JSX.Element { return ( @@ -34,10 +35,6 @@ export function SolidBgTextBg(): JSX.Element { ); } -SolidBgTextBg.story = { - name: 'Solid bg + text bg', -}; - export function Gradient(): JSX.Element { return ( ; + +export function Defaults(args: PropsType): JSX.Element { + return ; +} + +export function Long(args: PropsType): JSX.Element { + return ( + + Lorem ipsum dolor sit amet, consectetur adipisicing elit. Debitis deserunt + cupiditate doloribus vitae perspiciatis, eos atque mollitia aliquam quae + aspernatur et iure vero illo veritatis quibusdam maiores laborum. + Inventore, minus. + + ); +} diff --git a/ts/components/ToastAlreadyRequestedToJoin.stories.tsx b/ts/components/ToastAlreadyRequestedToJoin.stories.tsx index b8d61e73359c..01f397d33f2a 100644 --- a/ts/components/ToastAlreadyRequestedToJoin.stories.tsx +++ b/ts/components/ToastAlreadyRequestedToJoin.stories.tsx @@ -3,6 +3,8 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastAlreadyRequestedToJoin'; import { ToastAlreadyRequestedToJoin } from './ToastAlreadyRequestedToJoin'; import { setupI18n } from '../util/setupI18n'; @@ -17,12 +19,8 @@ const defaultProps = { export default { title: 'Components/ToastAlreadyRequestedToJoin', -}; +} satisfies Meta; export const _ToastAlreadyRequestedToJoin = (): JSX.Element => ( ); - -_ToastAlreadyRequestedToJoin.story = { - name: 'ToastAlreadyRequestedToJoin', -}; diff --git a/ts/components/ToastAlreadyRequestedToJoin.tsx b/ts/components/ToastAlreadyRequestedToJoin.tsx index 5a38cc2c5618..a35af967248d 100644 --- a/ts/components/ToastAlreadyRequestedToJoin.tsx +++ b/ts/components/ToastAlreadyRequestedToJoin.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastCaptchaFailed.stories.tsx b/ts/components/ToastCaptchaFailed.stories.tsx index 8cada788fbf6..7592b59c0aaf 100644 --- a/ts/components/ToastCaptchaFailed.stories.tsx +++ b/ts/components/ToastCaptchaFailed.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastCaptchaFailed'; import { ToastCaptchaFailed } from './ToastCaptchaFailed'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastCaptchaFailed', -}; +} satisfies Meta; export const _ToastCaptchaFailed = (): JSX.Element => ( ); - -_ToastCaptchaFailed.story = { - name: 'ToastCaptchaFailed', -}; diff --git a/ts/components/ToastCaptchaFailed.tsx b/ts/components/ToastCaptchaFailed.tsx index 907982181dfe..1fd0dd64e104 100644 --- a/ts/components/ToastCaptchaFailed.tsx +++ b/ts/components/ToastCaptchaFailed.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastCaptchaSolved.stories.tsx b/ts/components/ToastCaptchaSolved.stories.tsx index 8fba3fa66e73..838b9978998f 100644 --- a/ts/components/ToastCaptchaSolved.stories.tsx +++ b/ts/components/ToastCaptchaSolved.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastCaptchaSolved'; import { ToastCaptchaSolved } from './ToastCaptchaSolved'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastCaptchaSolved', -}; +} satisfies Meta; export const _ToastCaptchaSolved = (): JSX.Element => ( ); - -_ToastCaptchaSolved.story = { - name: 'ToastCaptchaSolved', -}; diff --git a/ts/components/ToastCaptchaSolved.tsx b/ts/components/ToastCaptchaSolved.tsx index 3d18dfada6ff..1b410fb1ccb5 100644 --- a/ts/components/ToastCaptchaSolved.tsx +++ b/ts/components/ToastCaptchaSolved.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastDebugLogError.stories.tsx b/ts/components/ToastDebugLogError.stories.tsx index 419e8eb92890..0197d49d86b6 100644 --- a/ts/components/ToastDebugLogError.stories.tsx +++ b/ts/components/ToastDebugLogError.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastDebugLogError'; import { ToastDebugLogError } from './ToastDebugLogError'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastDebugLogError', -}; +} satisfies Meta; export const _ToastDebugLogError = (): JSX.Element => ( ); - -_ToastDebugLogError.story = { - name: 'ToastDebugLogError', -}; diff --git a/ts/components/ToastDebugLogError.tsx b/ts/components/ToastDebugLogError.tsx index 10765032d1b7..c0c7f95a34fc 100644 --- a/ts/components/ToastDebugLogError.tsx +++ b/ts/components/ToastDebugLogError.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastFileSize.stories.tsx b/ts/components/ToastFileSize.stories.tsx index 4641897d8d28..eeca4dbb4d33 100644 --- a/ts/components/ToastFileSize.stories.tsx +++ b/ts/components/ToastFileSize.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastFileSize'; import { ToastFileSize } from './ToastFileSize'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastFileSize', -}; +} satisfies Meta; export const _ToastFileSize = (): JSX.Element => ( ); - -_ToastFileSize.story = { - name: 'ToastFileSize', -}; diff --git a/ts/components/ToastFileSize.tsx b/ts/components/ToastFileSize.tsx index 6baf2f26f864..76675693f35f 100644 --- a/ts/components/ToastFileSize.tsx +++ b/ts/components/ToastFileSize.tsx @@ -10,7 +10,7 @@ export type ToastPropsType = { units: string; }; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; } & ToastPropsType; diff --git a/ts/components/ToastGroupLinkCopied.stories.tsx b/ts/components/ToastGroupLinkCopied.stories.tsx index 905342faf522..5598c690dc20 100644 --- a/ts/components/ToastGroupLinkCopied.stories.tsx +++ b/ts/components/ToastGroupLinkCopied.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastGroupLinkCopied'; import { ToastGroupLinkCopied } from './ToastGroupLinkCopied'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastGroupLinkCopied', -}; +} satisfies Meta; export const _ToastGroupLinkCopied = (): JSX.Element => ( ); - -_ToastGroupLinkCopied.story = { - name: 'ToastGroupLinkCopied', -}; diff --git a/ts/components/ToastGroupLinkCopied.tsx b/ts/components/ToastGroupLinkCopied.tsx index d660ad69d942..e24adfdaf4c0 100644 --- a/ts/components/ToastGroupLinkCopied.tsx +++ b/ts/components/ToastGroupLinkCopied.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastInternalError.stories.tsx b/ts/components/ToastInternalError.stories.tsx index edc512cdd677..39b27e4d2f1c 100644 --- a/ts/components/ToastInternalError.stories.tsx +++ b/ts/components/ToastInternalError.stories.tsx @@ -3,11 +3,12 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastInternalError'; import { ToastInternalError, ToastInternalErrorKind, } from './ToastInternalError'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -21,7 +22,7 @@ const defaultProps = { export default { title: 'Components/ToastInternalError', -}; +} satisfies Meta; export function ToastDecryptionError(): JSX.Element { return ( @@ -34,10 +35,6 @@ export function ToastDecryptionError(): JSX.Element { ); } -ToastDecryptionError.story = { - name: 'ToastDecryptionError', -}; - export function ToastCDSMirroringError(): JSX.Element { return ( ); } - -ToastDecryptionError.story = { - name: 'ToastCDSMirroringError', -}; diff --git a/ts/components/ToastInternalError.tsx b/ts/components/ToastInternalError.tsx index 4aaf1239f042..d65c3557dc47 100644 --- a/ts/components/ToastInternalError.tsx +++ b/ts/components/ToastInternalError.tsx @@ -24,7 +24,7 @@ export type ToastPropsType = { } ); -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; } & ToastPropsType; diff --git a/ts/components/ToastLinkCopied.stories.tsx b/ts/components/ToastLinkCopied.stories.tsx index 3cdec4e44b38..48f83e16ca2e 100644 --- a/ts/components/ToastLinkCopied.stories.tsx +++ b/ts/components/ToastLinkCopied.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastLinkCopied'; import { ToastLinkCopied } from './ToastLinkCopied'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastLinkCopied', -}; +} satisfies Meta; export const _ToastLinkCopied = (): JSX.Element => ( ); - -_ToastLinkCopied.story = { - name: 'ToastLinkCopied', -}; diff --git a/ts/components/ToastLinkCopied.tsx b/ts/components/ToastLinkCopied.tsx index cf9fceb57f42..d4c30b651635 100644 --- a/ts/components/ToastLinkCopied.tsx +++ b/ts/components/ToastLinkCopied.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastLoadingFullLogs.stories.tsx b/ts/components/ToastLoadingFullLogs.stories.tsx index 04499d47d06c..b242468ad9c6 100644 --- a/ts/components/ToastLoadingFullLogs.stories.tsx +++ b/ts/components/ToastLoadingFullLogs.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastLoadingFullLogs'; import { ToastLoadingFullLogs } from './ToastLoadingFullLogs'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastLoadingFullLogs', -}; +} satisfies Meta; export const _ToastLoadingFullLogs = (): JSX.Element => ( ); - -_ToastLoadingFullLogs.story = { - name: 'ToastLoadingFullLogs', -}; diff --git a/ts/components/ToastLoadingFullLogs.tsx b/ts/components/ToastLoadingFullLogs.tsx index e2b4504c32bf..bfd6cd081f1e 100644 --- a/ts/components/ToastLoadingFullLogs.tsx +++ b/ts/components/ToastLoadingFullLogs.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastManager.stories.tsx b/ts/components/ToastManager.stories.tsx index fe39e138857b..4efddb6e7a02 100644 --- a/ts/components/ToastManager.stories.tsx +++ b/ts/components/ToastManager.stories.tsx @@ -1,9 +1,10 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React from 'react'; +import { action } from '@storybook/addon-actions'; import enMessages from '../../_locales/en/messages.json'; import { ToastManager } from './ToastManager'; import type { AnyToast } from '../types/Toast'; @@ -142,25 +143,23 @@ export default { title: 'Components/ToastManager', component: ToastManager, argTypes: { - hideToast: { action: true }, - openFileInFolder: { action: true }, - onUndoArchive: { action: true }, - i18n: { - defaultValue: i18n, - }, toastType: { - defaultValue: ToastType.AddingUserToGroup, options: ToastType, control: { type: 'select' }, }, - OS: { - defaultValue: 'macOS', - }, }, -} as Meta; + args: { + hideToast: action('hideToast'), + openFileInFolder: action('openFileInFolder'), + onUndoArchive: action('onUndoArchive'), + i18n, + toastType: ToastType.AddingUserToGroup, + OS: 'macOS', + }, +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => { +const Template: StoryFn = args => { const { toastType, ...rest } = args; return ( <> diff --git a/ts/components/ToastStickerPackInstallFailed.stories.tsx b/ts/components/ToastStickerPackInstallFailed.stories.tsx index 00fdf3e21900..09ec721d3c0f 100644 --- a/ts/components/ToastStickerPackInstallFailed.stories.tsx +++ b/ts/components/ToastStickerPackInstallFailed.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastStickerPackInstallFailed'; import { ToastStickerPackInstallFailed } from './ToastStickerPackInstallFailed'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastStickerPackInstallFailed', -}; +} satisfies Meta; export const _ToastStickerPackInstallFailed = (): JSX.Element => ( ); - -_ToastStickerPackInstallFailed.story = { - name: 'ToastStickerPackInstallFailed', -}; diff --git a/ts/components/ToastStickerPackInstallFailed.tsx b/ts/components/ToastStickerPackInstallFailed.tsx index ac353d1444cf..e396a684b1ae 100644 --- a/ts/components/ToastStickerPackInstallFailed.tsx +++ b/ts/components/ToastStickerPackInstallFailed.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastVoiceNoteLimit.stories.tsx b/ts/components/ToastVoiceNoteLimit.stories.tsx index 5f9b2e7711fa..c3a29f5863ac 100644 --- a/ts/components/ToastVoiceNoteLimit.stories.tsx +++ b/ts/components/ToastVoiceNoteLimit.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastVoiceNoteLimit'; import { ToastVoiceNoteLimit } from './ToastVoiceNoteLimit'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastVoiceNoteLimit', -}; +} satisfies Meta; export const _ToastVoiceNoteLimit = (): JSX.Element => ( ); - -_ToastVoiceNoteLimit.story = { - name: 'ToastVoiceNoteLimit', -}; diff --git a/ts/components/ToastVoiceNoteLimit.tsx b/ts/components/ToastVoiceNoteLimit.tsx index 2b63e2e6db69..a9d1e89dd4fe 100644 --- a/ts/components/ToastVoiceNoteLimit.tsx +++ b/ts/components/ToastVoiceNoteLimit.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/ToastVoiceNoteMustBeOnlyAttachment.stories.tsx b/ts/components/ToastVoiceNoteMustBeOnlyAttachment.stories.tsx index 4ce5f46e6e8b..4b71ce879658 100644 --- a/ts/components/ToastVoiceNoteMustBeOnlyAttachment.stories.tsx +++ b/ts/components/ToastVoiceNoteMustBeOnlyAttachment.stories.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ToastVoiceNoteMustBeOnlyAttachment'; import { ToastVoiceNoteMustBeOnlyAttachment } from './ToastVoiceNoteMustBeOnlyAttachment'; - import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; @@ -17,12 +18,8 @@ const defaultProps = { export default { title: 'Components/ToastVoiceNoteMustBeOnlyAttachment', -}; +} satisfies Meta; export const _ToastVoiceNoteMustBeOnlyAttachment = (): JSX.Element => ( ); - -_ToastVoiceNoteMustBeOnlyAttachment.story = { - name: 'ToastVoiceNoteMustBeOnlyAttachment', -}; diff --git a/ts/components/ToastVoiceNoteMustBeOnlyAttachment.tsx b/ts/components/ToastVoiceNoteMustBeOnlyAttachment.tsx index 846598267f40..f07f4cf367b6 100644 --- a/ts/components/ToastVoiceNoteMustBeOnlyAttachment.tsx +++ b/ts/components/ToastVoiceNoteMustBeOnlyAttachment.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { Toast } from './Toast'; -type PropsType = { +export type PropsType = { i18n: LocalizerType; onClose: () => unknown; }; diff --git a/ts/components/Tooltip.stories.tsx b/ts/components/Tooltip.stories.tsx index 4fb4c8e334a6..05165dc2fcaf 100644 --- a/ts/components/Tooltip.stories.tsx +++ b/ts/components/Tooltip.stories.tsx @@ -2,22 +2,32 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { select } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './Tooltip'; import { Tooltip, TooltipPlacement } from './Tooltip'; import { Theme } from '../util/theme'; -const createProps = (overrideProps: Partial = {}): PropsType => ({ - content: overrideProps.content || 'Hello World', - direction: select('direction', TooltipPlacement, overrideProps.direction), - sticky: overrideProps.sticky, - theme: overrideProps.theme, -}); - export default { title: 'Components/Tooltip', -}; + argTypes: { + content: { control: { type: 'text' } }, + direction: { + control: { type: 'select' }, + options: Object.values(TooltipPlacement), + }, + sticky: { control: { type: 'boolean' } }, + theme: { + control: { type: 'select' }, + options: Object.keys(Theme), + mappings: Theme, + }, + }, + args: { + content: 'Hello World', + direction: TooltipPlacement.Top, + sticky: false, + }, +} satisfies Meta; const Trigger = ( ); -export const _Top = (): JSX.Element => ( - - {Trigger} - -); - -export const _Right = (): JSX.Element => ( - - {Trigger} - -); - -export const _Bottom = (): JSX.Element => ( - - {Trigger} - -); - -export const _Left = (): JSX.Element => ( - - {Trigger} - -); - -export function Sticky(): JSX.Element { +export function Top(args: PropsType): JSX.Element { return ( - + {Trigger} ); } -export function WithAppliedPopperModifiers(): JSX.Element { +export function Right(args: PropsType): JSX.Element { + return ( + + {Trigger} + + ); +} + +export function Bottom(args: PropsType): JSX.Element { + return ( + + {Trigger} + + ); +} + +export function Left(args: PropsType): JSX.Element { + return ( + + {Trigger} + + ); +} + +export function Sticky(args: PropsType): JSX.Element { + return ( + + {Trigger} + + ); +} + +export function WithAppliedPopperModifiers(args: PropsType): JSX.Element { return ( + {Trigger} ); diff --git a/ts/components/UnsupportedOSDialog.stories.tsx b/ts/components/UnsupportedOSDialog.stories.tsx index 4392215d3234..824cefccc0d1 100644 --- a/ts/components/UnsupportedOSDialog.stories.tsx +++ b/ts/components/UnsupportedOSDialog.stories.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { UnsupportedOSDialog } from './UnsupportedOSDialog'; import type { PropsType } from './UnsupportedOSDialog'; import { setupI18n } from '../util/setupI18n'; @@ -57,7 +57,7 @@ const permutations: ReadonlyArray<{ export default { title: 'Components/UnsupportedOSDialog', -}; +} satisfies Meta; export function Iterations(): JSX.Element { return ( diff --git a/ts/components/UsernameLinkModalBody.stories.tsx b/ts/components/UsernameLinkModalBody.stories.tsx index 15d9f6fa4d31..a74069e4e964 100644 --- a/ts/components/UsernameLinkModalBody.stories.tsx +++ b/ts/components/UsernameLinkModalBody.stories.tsx @@ -2,8 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useCallback, useState } from 'react'; -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; import enMessages from '../../_locales/en/messages.json'; import { UsernameLinkState } from '../state/ducks/usernameEnums'; import { setupI18n } from '../util/setupI18n'; @@ -21,26 +22,18 @@ export default { component: UsernameLinkModalBody, title: 'Components/UsernameLinkModalBody', argTypes: { - i18n: { - defaultValue: i18n, - }, link: { control: { type: 'text' }, - defaultValue: - 'https://signal.me/#eu/n-AJkmmykrFB7j6UODGndSycxcMdp_v6ppRp9rFu5Ad39q_9Ngi_k9-TARWfT43t', }, username: { control: { type: 'text' }, - defaultValue: 'alice.12', }, usernameLinkState: { control: { type: 'select' }, - defaultValue: UsernameLinkState.Ready, options: [UsernameLinkState.Ready, UsernameLinkState.Updating], }, colorId: { control: { type: 'select' }, - defaultValue: ColorEnum.BLUE, mapping: { blue: ColorEnum.BLUE, white: ColorEnum.WHITE, @@ -52,17 +45,22 @@ export default { purple: ColorEnum.PURPLE, }, }, - showToast: { action: true }, - resetUsernameLink: { action: true }, - setUsernameLinkColor: { action: true }, - onBack: { action: true }, }, -} as Meta; - -type ArgsType = PropsType; + args: { + i18n, + link: 'https://signal.me/#eu/n-AJkmmykrFB7j6UODGndSycxcMdp_v6ppRp9rFu5Ad39q_9Ngi_k9-TARWfT43t', + username: 'alice.12', + usernameLinkState: UsernameLinkState.Ready, + colorId: ColorEnum.BLUE, + showToast: action('showToast'), + resetUsernameLink: action('resetUsernameLink'), + setUsernameLinkColor: action('setUsernameLinkColor'), + onBack: action('onBack'), + }, +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => { +const Template: StoryFn = args => { const [attachment, setAttachment] = useState(); const saveAttachment = useCallback(({ data }: { data?: Uint8Array }) => { if (!data) { @@ -93,13 +91,6 @@ const Template: Story = args => { }; export const Normal = Template.bind({}); -Normal.args = {}; -Normal.story = { - name: 'normal', -}; export const NoLink = Template.bind({}); NoLink.args = { link: '' }; -NoLink.story = { - name: 'normal', -}; diff --git a/ts/components/UsernameOnboardingModalBody.stories.tsx b/ts/components/UsernameOnboardingModalBody.stories.tsx index 4c6b346e1e46..ea597221032a 100644 --- a/ts/components/UsernameOnboardingModalBody.stories.tsx +++ b/ts/components/UsernameOnboardingModalBody.stories.tsx @@ -2,8 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; import enMessages from '../../_locales/en/messages.json'; import { setupI18n } from '../util/setupI18n'; @@ -15,23 +16,15 @@ const i18n = setupI18n('en', enMessages); export default { component: UsernameOnboardingModalBody, title: 'Components/UsernameOnboardingModalBody', - argTypes: { - i18n: { - defaultValue: i18n, - }, - onNext: { action: true }, + args: { + i18n, + onNext: action('onNext'), }, -} as Meta; - -type ArgsType = PropsType; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => { +const Template: StoryFn = args => { return ; }; export const Normal = Template.bind({}); -Normal.args = {}; -Normal.story = { - name: 'normal', -}; diff --git a/ts/components/WhatsNewModal.stories.tsx b/ts/components/WhatsNewModal.stories.tsx index fc24bd9beb22..69e7f5b5f3ed 100644 --- a/ts/components/WhatsNewModal.stories.tsx +++ b/ts/components/WhatsNewModal.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './WhatsNewModal'; import { WhatsNewModal } from './WhatsNewModal'; import enMessages from '../../_locales/en/messages.json'; @@ -13,7 +13,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/WhatsNewModal', -}; +} satisfies Meta; const getDefaultProps = (): PropsType => ({ hideWhatsNewModal: action('hideWhatsNewModal'), diff --git a/ts/components/conversation/AddNewLines.stories.tsx b/ts/components/conversation/AddNewLines.stories.tsx index 07f3fd318145..0f0545f3e586 100644 --- a/ts/components/conversation/AddNewLines.stories.tsx +++ b/ts/components/conversation/AddNewLines.stories.tsx @@ -2,12 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; +import type { Meta } from '@storybook/react'; import type { Props } from './AddNewLines'; import { AddNewLines } from './AddNewLines'; export default { title: 'Components/Conversation/AddNewLines', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ renderNonNewLine: overrideProps.renderNonNewLine, @@ -22,10 +23,6 @@ export function AllNewlines(): JSX.Element { return ; } -AllNewlines.story = { - name: 'All newlines', -}; - export function StartingEndingWithNewlines(): JSX.Element { const props = createProps({ text: '\nSome text\n', @@ -34,10 +31,6 @@ export function StartingEndingWithNewlines(): JSX.Element { return ; } -StartingEndingWithNewlines.story = { - name: 'Starting/Ending with Newlines', -}; - export function NewlinesInTheMiddle(): JSX.Element { const props = createProps({ text: 'Some\ntext', @@ -46,10 +39,6 @@ export function NewlinesInTheMiddle(): JSX.Element { return ; } -NewlinesInTheMiddle.story = { - name: 'Newlines in the Middle', -}; - export function NoNewlines(): JSX.Element { const props = createProps({ text: 'Some text', diff --git a/ts/components/conversation/AtMentionify.stories.tsx b/ts/components/conversation/AtMentionify.stories.tsx index 01b99192f0aa..513ed61e7f83 100644 --- a/ts/components/conversation/AtMentionify.stories.tsx +++ b/ts/components/conversation/AtMentionify.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { generateAci } from '../../types/ServiceId'; import type { Props } from './AtMentionify'; import { AtMentionify } from './AtMentionify'; @@ -18,7 +17,7 @@ const SERVICE_ID_6 = generateAci(); export default { title: 'Components/Conversation/AtMentionify', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ mentions: overrideProps.mentions, diff --git a/ts/components/conversation/AttachmentList.stories.tsx b/ts/components/conversation/AttachmentList.stories.tsx index 732e4f296c73..050b7f9c4f76 100644 --- a/ts/components/conversation/AttachmentList.stories.tsx +++ b/ts/components/conversation/AttachmentList.stories.tsx @@ -2,10 +2,12 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - -import type { AttachmentDraftType } from '../../types/Attachment'; +import type { Meta } from '@storybook/react'; +import type { + AttachmentDraftType, + AttachmentType, +} from '../../types/Attachment'; import type { Props } from './AttachmentList'; import { AttachmentList } from './AttachmentList'; import { @@ -17,14 +19,13 @@ import { } from '../../types/MIME'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; - import { fakeDraftAttachment } from '../../test-both/helpers/fakeAttachment'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/AttachmentList', -}; +} satisfies Meta>; const createProps = ( overrideProps: Partial> = {} @@ -110,10 +111,6 @@ export function MultipleWithNonVisualTypes(): JSX.Element { return ; } -MultipleWithNonVisualTypes.story = { - name: 'Multiple with Non-Visual Types', -}; - export function EmptyList(): JSX.Element { const props = createProps(); diff --git a/ts/components/conversation/CallingNotification.stories.tsx b/ts/components/conversation/CallingNotification.stories.tsx index 6f9504246d30..af184815dd0d 100644 --- a/ts/components/conversation/CallingNotification.stories.tsx +++ b/ts/components/conversation/CallingNotification.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import { CallMode } from '../../types/Calling'; @@ -26,7 +26,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/CallingNotification', -}; +} satisfies Meta; const getCommonProps = (options: { mode: CallMode; @@ -256,10 +256,6 @@ export function TwoIncomingDirectCallsBackToBack(): JSX.Element { ); } -TwoIncomingDirectCallsBackToBack.story = { - name: 'Two incoming direct calls back-to-back', -}; - export function TwoOutgoingDirectCallsBackToBack(): JSX.Element { return ( <> @@ -290,10 +286,6 @@ export function TwoOutgoingDirectCallsBackToBack(): JSX.Element { ); } -TwoOutgoingDirectCallsBackToBack.story = { - name: 'Two outgoing direct calls back-to-back', -}; - export function GroupCallByUnknown(): JSX.Element { return ( ); } - -GroupCallEnded.story = { - name: 'Group call: ended', -}; diff --git a/ts/components/conversation/ChangeNumberNotification.stories.tsx b/ts/components/conversation/ChangeNumberNotification.stories.tsx index a2fa44a74d0e..97c173d30736 100644 --- a/ts/components/conversation/ChangeNumberNotification.stories.tsx +++ b/ts/components/conversation/ChangeNumberNotification.stories.tsx @@ -2,16 +2,16 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; - +import type { Props } from './ChangeNumberNotification'; import { ChangeNumberNotification } from './ChangeNumberNotification'; export default { title: 'Components/Conversation/ChangeNumberNotification', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); @@ -36,7 +36,3 @@ export function LongName(): JSX.Element { /> ); } - -LongName.story = { - name: 'Long name', -}; diff --git a/ts/components/conversation/ChatSessionRefreshedDialog.stories.tsx b/ts/components/conversation/ChatSessionRefreshedDialog.stories.tsx index 43eb24d5eff1..9570fa85240b 100644 --- a/ts/components/conversation/ChatSessionRefreshedDialog.stories.tsx +++ b/ts/components/conversation/ChatSessionRefreshedDialog.stories.tsx @@ -3,16 +3,17 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import type { PropsType } from './ChatSessionRefreshedDialog'; import { ChatSessionRefreshedDialog } from './ChatSessionRefreshedDialog'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ChatSessionRefreshedDialog', -}; +} satisfies Meta; export function Default(): JSX.Element { return ( diff --git a/ts/components/conversation/ChatSessionRefreshedNotification.stories.tsx b/ts/components/conversation/ChatSessionRefreshedNotification.stories.tsx index ebe5884bac53..0441e86ced1d 100644 --- a/ts/components/conversation/ChatSessionRefreshedNotification.stories.tsx +++ b/ts/components/conversation/ChatSessionRefreshedNotification.stories.tsx @@ -2,16 +2,17 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import type { PropsType } from './ChatSessionRefreshedNotification'; import { ChatSessionRefreshedNotification } from './ChatSessionRefreshedNotification'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ChatSessionRefreshedNotification', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/conversation/ContactDetail.stories.tsx b/ts/components/conversation/ContactDetail.stories.tsx index 3c25a2cc93d0..b1c2af07ea0d 100644 --- a/ts/components/conversation/ContactDetail.stories.tsx +++ b/ts/components/conversation/ContactDetail.stories.tsx @@ -2,23 +2,21 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { Props } from './ContactDetail'; import { ContactDetail } from './ContactDetail'; import { AddressType, ContactFormType } from '../../types/EmbeddedContact'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import { IMAGE_GIF } from '../../types/MIME'; - import { fakeAttachment } from '../../test-both/helpers/fakeAttachment'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ContactDetail', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ contact: overrideProps.contact || {}, @@ -192,10 +190,6 @@ export function GivenFamilyName(): JSX.Element { return ; } -GivenFamilyName.story = { - name: 'Given + Family Name', -}; - export function FamilyName(): JSX.Element { const props = createProps({ contact: { @@ -232,17 +226,9 @@ export function EmptyWithAccount(): JSX.Element { return ; } -EmptyWithAccount.story = { - name: 'Empty with Account', -}; - export function EmptyWithoutAccount(): JSX.Element { const props = createProps({ hasSignalAccount: false, }); return ; } - -EmptyWithoutAccount.story = { - name: 'Empty without Account', -}; diff --git a/ts/components/conversation/ContactModal.stories.tsx b/ts/components/conversation/ContactModal.stories.tsx index 78d97014efa6..a5af37e00e60 100644 --- a/ts/components/conversation/ContactModal.stories.tsx +++ b/ts/components/conversation/ContactModal.stories.tsx @@ -1,10 +1,10 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import * as React from 'react'; import casual from 'casual'; - +import { action } from '@storybook/addon-actions'; import type { ConversationType } from '../../state/ducks/conversations'; import type { PropsType } from './ContactModal'; import enMessages from '../../../_locales/en/messages.json'; @@ -31,65 +31,41 @@ const defaultGroup: ConversationType = getDefaultConversation({ export default { title: 'Components/Conversation/ContactModal', component: ContactModal, - argTypes: { - i18n: { - defaultValue: i18n, - }, - areWeASubscriber: { - defaultValue: false, - }, - areWeAdmin: { - defaultValue: false, - }, - badges: { - defaultValue: [], - }, - contact: { - defaultValue: defaultContact, - }, - conversation: { - defaultValue: defaultGroup, - }, - hasStories: { - defaultValue: undefined, - }, - hideContactModal: { action: true }, - isAdmin: { - defaultValue: false, - }, - isMember: { - defaultValue: true, - }, - removeMemberFromGroup: { action: true }, - showConversation: { action: true }, - theme: { - defaultValue: ThemeType.light, - }, - toggleAdmin: { action: true }, - toggleSafetyNumberModal: { action: true }, - updateConversationModelSharedGroups: { action: true }, - viewUserStories: { action: true }, + args: { + i18n, + areWeASubscriber: false, + areWeAdmin: false, + badges: [], + contact: defaultContact, + conversation: defaultGroup, + hasStories: undefined, + hideContactModal: action('hideContactModal'), + isAdmin: false, + isMember: true, + removeMemberFromGroup: action('removeMemberFromGroup'), + showConversation: action('showConversation'), + theme: ThemeType.light, + toggleAdmin: action('toggleAdmin'), + toggleSafetyNumberModal: action('toggleSafetyNumberModal'), + updateConversationModelSharedGroups: action( + 'updateConversationModelSharedGroups' + ), + viewUserStories: action('viewUserStories'), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const AsNonAdmin = Template.bind({}); AsNonAdmin.args = { areWeAdmin: false, }; -AsNonAdmin.story = { - name: 'As non-admin', -}; export const AsAdmin = Template.bind({}); AsAdmin.args = { areWeAdmin: true, }; -AsAdmin.story = { - name: 'As admin', -}; export const AsAdminWithNoGroupLink = Template.bind({}); AsAdminWithNoGroupLink.args = { @@ -99,17 +75,11 @@ AsAdminWithNoGroupLink.args = { groupLink: undefined, }, }; -AsAdminWithNoGroupLink.story = { - name: 'As admin with no group link', -}; export const AsAdminViewingNonMemberOfGroup = Template.bind({}); AsAdminViewingNonMemberOfGroup.args = { isMember: false, }; -AsAdminViewingNonMemberOfGroup.story = { - name: 'As admin, viewing non-member of group', -}; export const WithoutPhoneNumber = Template.bind({}); WithoutPhoneNumber.args = { @@ -118,9 +88,6 @@ WithoutPhoneNumber.args = { phoneNumber: undefined, }, }; -WithoutPhoneNumber.story = { - name: 'Without phone number', -}; export const ViewingSelf = Template.bind({}); ViewingSelf.args = { @@ -129,17 +96,11 @@ ViewingSelf.args = { isMe: true, }, }; -ViewingSelf.story = { - name: 'Viewing self', -}; export const WithBadges = Template.bind({}); WithBadges.args = { badges: getFakeBadges(2), }; -WithBadges.story = { - name: 'With badges', -}; export const WithUnreadStories = Template.bind({}); WithUnreadStories.args = { diff --git a/ts/components/conversation/ContactName.stories.tsx b/ts/components/conversation/ContactName.stories.tsx index 8ce5763c62b9..33ce1d3f5b38 100644 --- a/ts/components/conversation/ContactName.stories.tsx +++ b/ts/components/conversation/ContactName.stories.tsx @@ -2,22 +2,19 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; +import type { PropsType } from './ContactName'; import { ContactName } from './ContactName'; import { ContactNameColors } from '../../types/Colors'; export default { title: 'Components/Conversation/ContactName', -}; +} satisfies Meta; export function FirstNameAndTitleTitlePreferred(): JSX.Element { return ; } -FirstNameAndTitleTitlePreferred.story = { - name: 'First name and title; title preferred', -}; - export function FirstNameAndTitleFirstNamePreferred(): JSX.Element { return ( diff --git a/ts/components/conversation/ContactSpoofingReviewDialog.stories.tsx b/ts/components/conversation/ContactSpoofingReviewDialog.stories.tsx index f9cc4e7fcb19..77533746bb0d 100644 --- a/ts/components/conversation/ContactSpoofingReviewDialog.stories.tsx +++ b/ts/components/conversation/ContactSpoofingReviewDialog.stories.tsx @@ -4,10 +4,11 @@ import React from 'react'; import { times } from 'lodash'; import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; - +import type { PropsType } from './ContactSpoofingReviewDialog'; import { ContactSpoofingReviewDialog } from './ContactSpoofingReviewDialog'; import { ContactSpoofingType } from '../../util/contactSpoofing'; import { ThemeType } from '../../types/Util'; @@ -16,7 +17,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ContactSpoofingReviewDialog', -}; +} satisfies Meta; const getCommonProps = () => ({ acceptConversation: action('acceptConversation'), @@ -44,10 +45,6 @@ export function DirectConversationsWithSameTitle(): JSX.Element { ); } -DirectConversationsWithSameTitle.story = { - name: 'Direct conversations with same title', -}; - export function NotAdmin(): JSX.Element { return ( ); } - -Admin.story = { - name: 'Group conversation many group members, and we are admin', -}; diff --git a/ts/components/conversation/ConversationHeader.stories.tsx b/ts/components/conversation/ConversationHeader.stories.tsx index 18c72b8ce2fd..0bc14f34b5d0 100644 --- a/ts/components/conversation/ConversationHeader.stories.tsx +++ b/ts/components/conversation/ConversationHeader.stories.tsx @@ -3,15 +3,15 @@ import type { ComponentProps } from 'react'; import React, { useContext } from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; import { getRandomColor } from '../../test-both/helpers/getRandomColor'; import { setupI18n } from '../../util/setupI18n'; import { DurationInSeconds } from '../../util/durations'; import enMessages from '../../../_locales/en/messages.json'; import { StorybookThemeContext } from '../../../.storybook/StorybookThemeContext'; +import type { PropsType } from './ConversationHeader'; import { ConversationHeader, OutgoingCallButtonStyle, @@ -20,7 +20,7 @@ import { gifUrl } from '../../storybook/Fixtures'; export default { title: 'Components/Conversation/ConversationHeader', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); @@ -216,10 +216,6 @@ export function PrivateConvo(): JSX.Element { ); } -PrivateConvo.story = { - name: '1:1 conversation', -}; - export function Group(): JSX.Element { const items: ItemsType = [ { @@ -302,11 +298,7 @@ export function Group(): JSX.Element { ); } -Group.story = { - name: 'In a group', -}; - -function NoteToSelf() { +export function NoteToSelf(): JSX.Element { const items: ItemsType = [ { title: 'In chat with yourself', @@ -340,11 +332,7 @@ function NoteToSelf() { ); } -NoteToSelf.story = { - name: 'Note to Self', -}; - -function Unaccepted() { +export function Unaccepted(): JSX.Element { const items: ItemsType = [ { title: '1:1 conversation', @@ -377,7 +365,3 @@ function Unaccepted() { ); } - -Unaccepted.story = { - name: 'Unaccepted', -}; diff --git a/ts/components/conversation/ConversationHero.stories.tsx b/ts/components/conversation/ConversationHero.stories.tsx index fb763928daf7..b578a473f282 100644 --- a/ts/components/conversation/ConversationHero.stories.tsx +++ b/ts/components/conversation/ConversationHero.stories.tsx @@ -1,10 +1,10 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import React, { useContext } from 'react'; import casual from 'casual'; - +import { action } from '@storybook/addon-actions'; import enMessages from '../../../_locales/en/messages.json'; import type { Props } from './ConversationHero'; import { ConversationHero } from './ConversationHero'; @@ -19,24 +19,18 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationHero', component: ConversationHero, - argTypes: { - conversationType: { - defaultValue: 'direct', - }, - i18n: { - defaultValue: i18n, - }, - theme: { - defaultValue: ThemeType.light, - }, - unblurAvatar: { action: true }, - updateSharedGroups: { action: true }, - viewUserStories: { action: true }, + args: { + conversationType: 'direct', + i18n, + theme: ThemeType.light, + unblurAvatar: action('unblurAvatar'), + updateSharedGroups: action('updateSharedGroups'), + viewUserStories: action('viewUserStories'), }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => { +const Template: StoryFn = args => { const theme = useContext(StorybookThemeContext); return (
@@ -49,57 +43,36 @@ export const DirectFiveOtherGroups = Template.bind({}); DirectFiveOtherGroups.args = { sharedGroupNames: Array.from(Array(5), () => casual.title), }; -DirectFiveOtherGroups.story = { - name: 'Direct (Five Other Groups)', -}; export const DirectFourOtherGroups = Template.bind({}); DirectFourOtherGroups.args = { sharedGroupNames: Array.from(Array(4), () => casual.title), }; -DirectFourOtherGroups.story = { - name: 'Direct (Four Other Groups)', -}; export const DirectThreeOtherGroups = Template.bind({}); DirectThreeOtherGroups.args = { sharedGroupNames: Array.from(Array(3), () => casual.title), }; -DirectThreeOtherGroups.story = { - name: 'Direct (Three Other Groups)', -}; export const DirectTwoOtherGroups = Template.bind({}); DirectTwoOtherGroups.args = { sharedGroupNames: Array.from(Array(2), () => casual.title), }; -DirectTwoOtherGroups.story = { - name: 'Direct (Two Other Groups)', -}; export const DirectOneOtherGroup = Template.bind({}); DirectOneOtherGroup.args = { sharedGroupNames: [casual.title], }; -DirectOneOtherGroup.story = { - name: 'Direct (One Other Group)', -}; export const DirectNoGroupsName = Template.bind({}); DirectNoGroupsName.args = { about: '👍 Free to chat', }; -DirectNoGroupsName.story = { - name: 'Direct (No Groups, Name)', -}; export const DirectNoGroupsJustProfile = Template.bind({}); DirectNoGroupsJustProfile.args = { phoneNumber: casual.phone, }; -DirectNoGroupsJustProfile.story = { - name: 'Direct (No Groups, Just Profile)', -}; export const DirectNoGroupsJustPhoneNumber = Template.bind({}); DirectNoGroupsJustPhoneNumber.args = { @@ -107,9 +80,6 @@ DirectNoGroupsJustPhoneNumber.args = { profileName: '', title: '', }; -DirectNoGroupsJustPhoneNumber.story = { - name: 'Direct (No Groups, Just Phone Number)', -}; export const DirectNoGroupsNoData = Template.bind({}); DirectNoGroupsNoData.args = { @@ -118,9 +88,6 @@ DirectNoGroupsNoData.args = { profileName: '', title: '', }; -DirectNoGroupsNoData.story = { - name: 'Direct (No Groups, No Data)', -}; export const DirectNoGroupsNoDataNotAccepted = Template.bind({}); DirectNoGroupsNoDataNotAccepted.args = { @@ -130,9 +97,6 @@ DirectNoGroupsNoDataNotAccepted.args = { profileName: '', title: '', }; -DirectNoGroupsNoDataNotAccepted.story = { - name: 'Direct (No Groups, No Data, Not Accepted)', -}; export const DirectNoGroupsNotAcceptedWithAvatar = Template.bind({}); DirectNoGroupsNotAcceptedWithAvatar.args = { @@ -140,9 +104,6 @@ DirectNoGroupsNotAcceptedWithAvatar.args = { acceptedMessageRequest: false, profileName: '', }; -DirectNoGroupsNotAcceptedWithAvatar.story = { - name: 'Direct (No Groups, No Data, Not Accepted, With Avatar)', -}; export const GroupManyMembers = Template.bind({}); GroupManyMembers.args = { @@ -151,9 +112,6 @@ GroupManyMembers.args = { membersCount: casual.integer(20, 100), title: casual.title, }; -GroupManyMembers.story = { - name: 'Group (many members)', -}; export const GroupOneMember = Template.bind({}); GroupOneMember.args = { @@ -163,9 +121,6 @@ GroupOneMember.args = { membersCount: 1, title: casual.title, }; -GroupOneMember.story = { - name: 'Group (one member)', -}; export const GroupZeroMembers = Template.bind({}); GroupZeroMembers.args = { @@ -175,9 +130,6 @@ GroupZeroMembers.args = { membersCount: 0, title: casual.title, }; -GroupZeroMembers.story = { - name: 'Group (zero members)', -}; export const GroupLongGroupDescription = Template.bind({}); GroupLongGroupDescription.args = { @@ -187,9 +139,6 @@ GroupLongGroupDescription.args = { membersCount: casual.integer(1, 10), title: casual.title, }; -GroupLongGroupDescription.story = { - name: 'Group (long group description)', -}; export const GroupNoName = Template.bind({}); GroupNoName.args = { @@ -197,17 +146,11 @@ GroupNoName.args = { membersCount: 0, title: '', }; -GroupNoName.story = { - name: 'Group (No name)', -}; export const NoteToSelf = Template.bind({}); NoteToSelf.args = { isMe: true, }; -NoteToSelf.story = { - name: 'Note to Self', -}; export const UnreadStories = Template.bind({}); UnreadStories.args = { diff --git a/ts/components/conversation/ConversationMergeNotification.stories.tsx b/ts/components/conversation/ConversationMergeNotification.stories.tsx index e32a6d636896..1aa6a6db56ef 100644 --- a/ts/components/conversation/ConversationMergeNotification.stories.tsx +++ b/ts/components/conversation/ConversationMergeNotification.stories.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { PropsType } from './ConversationMergeNotification'; @@ -12,7 +12,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationMergeNotification', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): PropsType => ({ i18n, diff --git a/ts/components/conversation/DeliveryIssueDialog.stories.tsx b/ts/components/conversation/DeliveryIssueDialog.stories.tsx index 4a69f4329265..c0a3a2d03bce 100644 --- a/ts/components/conversation/DeliveryIssueDialog.stories.tsx +++ b/ts/components/conversation/DeliveryIssueDialog.stories.tsx @@ -3,9 +3,10 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import type { PropsType } from './DeliveryIssueDialog'; import { DeliveryIssueDialog } from './DeliveryIssueDialog'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; @@ -14,7 +15,7 @@ const sender = getDefaultConversation(); export default { title: 'Components/Conversation/DeliveryIssueDialog', -}; +} satisfies Meta; export function Default(): JSX.Element { return ( diff --git a/ts/components/conversation/DeliveryIssueNotification.stories.tsx b/ts/components/conversation/DeliveryIssueNotification.stories.tsx index 9cfeb5d4ccfe..6bf736cddb70 100644 --- a/ts/components/conversation/DeliveryIssueNotification.stories.tsx +++ b/ts/components/conversation/DeliveryIssueNotification.stories.tsx @@ -2,15 +2,16 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import type { PropsType } from './DeliveryIssueNotification'; import { DeliveryIssueNotification } from './DeliveryIssueNotification'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; export default { title: 'Components/Conversation/DeliveryIssueNotification', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); const sender = getDefaultConversation(); @@ -37,10 +38,6 @@ export function WithALongName(): JSX.Element { ); } -WithALongName.story = { - name: 'With a long name', -}; - export function InGroup(): JSX.Element { return ; } diff --git a/ts/components/conversation/Emojify.stories.tsx b/ts/components/conversation/Emojify.stories.tsx index 1afbb2a993bf..106bd8dca31a 100644 --- a/ts/components/conversation/Emojify.stories.tsx +++ b/ts/components/conversation/Emojify.stories.tsx @@ -2,12 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; +import type { Meta } from '@storybook/react'; import type { Props } from './Emojify'; import { Emojify } from './Emojify'; export default { title: 'Components/Conversation/Emojify', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ renderNonEmoji: overrideProps.renderNonEmoji, @@ -92,10 +93,6 @@ export function AllTextNoEmoji(): JSX.Element { return ; } -AllTextNoEmoji.story = { - name: 'All Text, No Emoji', -}; - export function CustomTextRender(): JSX.Element { const props = createProps({ text: 'this 😹 cat 😹 is 😹 so 😹 joyful', @@ -117,10 +114,6 @@ export function TensOfThousandsOfEmoji(): JSX.Element { return ; } -TensOfThousandsOfEmoji.story = { - name: 'Tens of thousands of emoji', -}; - export function TensOfThousandsOfEmojiInterspersedWithText(): JSX.Element { const props = createProps({ text: '💅 hi '.repeat(40000), @@ -128,7 +121,3 @@ export function TensOfThousandsOfEmojiInterspersedWithText(): JSX.Element { return ; } - -TensOfThousandsOfEmojiInterspersedWithText.story = { - name: 'Tens of thousands of emoji, interspersed with text', -}; diff --git a/ts/components/conversation/ErrorBoundary.stories.tsx b/ts/components/conversation/ErrorBoundary.stories.tsx index b3b24b9075c4..8e574ae52144 100644 --- a/ts/components/conversation/ErrorBoundary.stories.tsx +++ b/ts/components/conversation/ErrorBoundary.stories.tsx @@ -3,16 +3,17 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import type { Props } from './ErrorBoundary'; import { ErrorBoundary } from './ErrorBoundary'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ErrorBoundary', -}; +} satisfies Meta; const Fail: React.FC> = () => { throw new Error('Failed'); @@ -25,7 +26,3 @@ export function ErrorState(): JSX.Element { ); } - -ErrorState.story = { - name: 'Error state', -}; diff --git a/ts/components/conversation/ExpireTimer.stories.tsx b/ts/components/conversation/ExpireTimer.stories.tsx index fd5283d6c5e4..87c0b63469c6 100644 --- a/ts/components/conversation/ExpireTimer.stories.tsx +++ b/ts/components/conversation/ExpireTimer.stories.tsx @@ -2,12 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; +import type { Meta } from '@storybook/react'; import type { Props } from './ExpireTimer'; import { ExpireTimer } from './ExpireTimer'; export default { title: 'Components/Conversation/ExpireTimer', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ direction: overrideProps.direction || 'outgoing', @@ -25,10 +26,6 @@ export const _30Seconds = (): JSX.Element => { return ; }; -_30Seconds.story = { - name: '30 seconds', -}; - export const _2Minutes = (): JSX.Element => { const twoMinutes = 60 * 1000 * 2; const props = createProps({ @@ -39,10 +36,6 @@ export const _2Minutes = (): JSX.Element => { return ; }; -_2Minutes.story = { - name: '2 minutes', -}; - export function InProgress(): JSX.Element { const props = createProps({ expirationTimestamp: Date.now() + 15 * 1000, diff --git a/ts/components/conversation/GroupDescription.stories.tsx b/ts/components/conversation/GroupDescription.stories.tsx index 45b4510ec2b9..01dd57e71a98 100644 --- a/ts/components/conversation/GroupDescription.stories.tsx +++ b/ts/components/conversation/GroupDescription.stories.tsx @@ -2,9 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - -import { text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { PropsType } from './GroupDescription'; import { GroupDescription } from './GroupDescription'; import { setupI18n } from '../../util/setupI18n'; @@ -14,80 +12,57 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/GroupDescription', -}; + argTypes: { + title: { control: { type: 'text' } }, + text: { control: { type: 'text' } }, + }, + args: { + i18n, + title: 'Sample Title', + text: 'Default group description', + }, +} satisfies Meta; -const createProps = (overrideProps: Partial = {}): PropsType => ({ - i18n, - title: text('title', overrideProps.title || 'Sample Title'), - text: text('text', overrideProps.text || 'Default group description'), -}); - -export function Default(): JSX.Element { - return ; +export function Default(args: PropsType): JSX.Element { + return ; } -export function Long(): JSX.Element { +export function Long(args: PropsType): JSX.Element { return ( ); } -export function WithNewlines(): JSX.Element { +export function WithNewlines(args: PropsType): JSX.Element { return ( ); } -WithNewlines.story = { - name: 'With newlines', -}; +export function WithEmoji(args: PropsType): JSX.Element { + return ; +} -export function WithEmoji(): JSX.Element { +export function WithLink(args: PropsType): JSX.Element { return ( ); } -WithEmoji.story = { - name: 'With emoji', -}; - -export function WithLink(): JSX.Element { +export function KitchenSink(args: PropsType): JSX.Element { return ( ); } - -WithLink.story = { - name: 'With link', -}; - -export function KitchenSink(): JSX.Element { - return ( - - ); -} - -KitchenSink.story = { - name: 'Kitchen sink', -}; diff --git a/ts/components/conversation/GroupNotification.stories.tsx b/ts/components/conversation/GroupNotification.stories.tsx index 2f6eaf83a7a7..e90d2e6c9d2d 100644 --- a/ts/components/conversation/GroupNotification.stories.tsx +++ b/ts/components/conversation/GroupNotification.stories.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { Props } from './GroupNotification'; @@ -11,7 +11,7 @@ import { getDefaultConversation } from '../../test-both/helpers/getDefaultConver export default { title: 'Components/Conversation', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); @@ -394,7 +394,3 @@ export const _GroupNotification = (): JSX.Element => ( ))} ); - -_GroupNotification.story = { - name: 'GroupNotification', -}; diff --git a/ts/components/conversation/GroupV1DisabledActions.stories.tsx b/ts/components/conversation/GroupV1DisabledActions.stories.tsx index f518d7654706..39a098d0502f 100644 --- a/ts/components/conversation/GroupV1DisabledActions.stories.tsx +++ b/ts/components/conversation/GroupV1DisabledActions.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { PropsType as GroupV1DisabledActionsPropsType } from './GroupV1DisabledActions'; import { GroupV1DisabledActions } from './GroupV1DisabledActions'; import { setupI18n } from '../../util/setupI18n'; @@ -19,7 +19,7 @@ const createProps = (): GroupV1DisabledActionsPropsType => ({ export default { title: 'Components/Conversation/GroupV1DisabledActions', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/conversation/GroupV1Migration.stories.tsx b/ts/components/conversation/GroupV1Migration.stories.tsx index 17a699cf3084..294d46713f28 100644 --- a/ts/components/conversation/GroupV1Migration.stories.tsx +++ b/ts/components/conversation/GroupV1Migration.stories.tsx @@ -2,9 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { isBoolean } from 'lodash'; -import { boolean } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; @@ -26,101 +24,62 @@ const contact2 = getDefaultConversation({ id: 'guid-2', }); -const createProps = (overrideProps: Partial = {}): PropsType => ({ - areWeInvited: boolean( - 'areWeInvited', - isBoolean(overrideProps.areWeInvited) ? overrideProps.areWeInvited : false - ), - conversationId: '123', - droppedMembers: overrideProps.droppedMembers || [contact1], - getPreferredBadge: () => undefined, - i18n, - invitedMembers: overrideProps.invitedMembers || [contact2], - theme: ThemeType.light, -}); - export default { title: 'Components/Conversation/GroupV1Migration', -}; + argTypes: { + areWeInvited: { control: { type: 'boolean' } }, + }, + args: { + areWeInvited: false, + conversationId: '123', + droppedMembers: [contact1], + getPreferredBadge: () => undefined, + i18n, + invitedMembers: [contact2], + theme: ThemeType.light, + }, +} satisfies Meta; -export function YouWereInvited(): JSX.Element { +export function YouWereInvited(args: PropsType): JSX.Element { + return ; +} + +export function SingleDroppedAndSingleInvitedMember( + args: PropsType +): JSX.Element { + return ; +} + +export function MultipleDroppedAndInvitedMembers(args: PropsType): JSX.Element { return ( ); } -YouWereInvited.story = { - name: 'You were invited', -}; - -export function SingleDroppedAndSingleInvitedMember(): JSX.Element { - return ; -} - -SingleDroppedAndSingleInvitedMember.story = { - name: 'Single dropped and single invited member', -}; - -export function MultipleDroppedAndInvitedMembers(): JSX.Element { +export function JustInvitedMembers(args: PropsType): JSX.Element { return ( ); } -MultipleDroppedAndInvitedMembers.story = { - name: 'Multiple dropped and invited members', -}; - -export function JustInvitedMembers(): JSX.Element { +export function JustDroppedMembers(args: PropsType): JSX.Element { return ( ); } -JustInvitedMembers.story = { - name: 'Just invited members', -}; - -export function JustDroppedMembers(): JSX.Element { - return ( - - ); +export function NoDroppedOrInvitedMembers(args: PropsType): JSX.Element { + return ; } - -JustDroppedMembers.story = { - name: 'Just dropped members', -}; - -export function NoDroppedOrInvitedMembers(): JSX.Element { - return ( - - ); -} - -NoDroppedOrInvitedMembers.story = { - name: 'No dropped or invited members', -}; diff --git a/ts/components/conversation/GroupV2Change.stories.tsx b/ts/components/conversation/GroupV2Change.stories.tsx index 07db88565be3..00657461d8b1 100644 --- a/ts/components/conversation/GroupV2Change.stories.tsx +++ b/ts/components/conversation/GroupV2Change.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import { generateAci, generatePni } from '../../types/ServiceId'; import type { ServiceIdString, AciString } from '../../types/ServiceId'; @@ -11,6 +11,7 @@ import enMessages from '../../../_locales/en/messages.json'; import type { GroupV2ChangeType } from '../../groups'; import { SignalService as Proto } from '../../protobuf'; import type { SmartContactRendererType } from '../../groupChange'; +import type { PropsType } from './GroupV2Change'; import { GroupV2Change } from './GroupV2Change'; import type { FullJSXType } from '../Intl'; @@ -69,7 +70,7 @@ const renderChange = ( export default { title: 'Components/Conversation/GroupV2Change', -}; +} satisfies Meta; export function Multiple(): JSX.Element { return ( @@ -313,10 +314,6 @@ export function AccessAttributes(): JSX.Element { ); } -AccessAttributes.story = { - name: 'Access (Attributes)', -}; - export function AccessMembers(): JSX.Element { return ( <> @@ -376,10 +373,6 @@ export function AccessMembers(): JSX.Element { ); } -AccessMembers.story = { - name: 'Access (Members)', -}; - export function AccessInviteLink(): JSX.Element { return ( <> @@ -439,10 +432,6 @@ export function AccessInviteLink(): JSX.Element { ); } -AccessInviteLink.story = { - name: 'Access (Invite Link)', -}; - export function MemberAdd(): JSX.Element { return ( <> @@ -619,10 +608,6 @@ export function MemberAddFromInvited(): JSX.Element { ); } -MemberAddFromInvited.story = { - name: 'Member Add (from invited)', -}; - export function MemberAddFromLink(): JSX.Element { return ( <> @@ -656,10 +641,6 @@ export function MemberAddFromLink(): JSX.Element { ); } -MemberAddFromLink.story = { - name: 'Member Add (from link)', -}; - export function MemberAddFromAdminApproval(): JSX.Element { return ( <> @@ -710,10 +691,6 @@ export function MemberAddFromAdminApproval(): JSX.Element { ); } -MemberAddFromAdminApproval.story = { - name: 'Member Add (from admin approval)', -}; - export function MemberRemove(): JSX.Element { return ( <> @@ -935,10 +912,6 @@ export function PendingAddOne(): JSX.Element { ); } -PendingAddOne.story = { - name: 'Pending Add - one', -}; - export function PendingAddMany(): JSX.Element { return ( <> @@ -972,10 +945,6 @@ export function PendingAddMany(): JSX.Element { ); } -PendingAddMany.story = { - name: 'Pending Add - many', -}; - export function PendingRemoveOne(): JSX.Element { return ( <> @@ -1119,10 +1088,6 @@ export function PendingRemoveOne(): JSX.Element { ); } -PendingRemoveOne.story = { - name: 'Pending Remove - one', -}; - export function PendingRemoveMany(): JSX.Element { return ( <> @@ -1215,10 +1180,6 @@ export function PendingRemoveMany(): JSX.Element { ); } -PendingRemoveMany.story = { - name: 'Pending Remove - many', -}; - export function AdminApprovalAdd(): JSX.Element { return ( <> @@ -1242,10 +1203,6 @@ export function AdminApprovalAdd(): JSX.Element { ); } -AdminApprovalAdd.story = { - name: 'Admin Approval (Add)', -}; - export function AdminApprovalRemove(): JSX.Element { return ( <> @@ -1279,10 +1236,6 @@ export function AdminApprovalRemove(): JSX.Element { ); } -AdminApprovalRemove.story = { - name: 'Admin Approval (Remove)', -}; - export function AdminApprovalBounce(): JSX.Element { return ( <> @@ -1384,10 +1337,6 @@ export function AdminApprovalBounce(): JSX.Element { ); } -AdminApprovalBounce.story = { - name: 'Admin Approval (Bounce)', -}; - export function GroupLinkAdd(): JSX.Element { return ( <> @@ -1447,10 +1396,6 @@ export function GroupLinkAdd(): JSX.Element { ); } -GroupLinkAdd.story = { - name: 'Group Link (Add)', -}; - export function GroupLinkReset(): JSX.Element { return ( <> @@ -1481,10 +1426,6 @@ export function GroupLinkReset(): JSX.Element { ); } -GroupLinkReset.story = { - name: 'Group Link (Reset)', -}; - export function GroupLinkRemove(): JSX.Element { return ( <> @@ -1515,10 +1456,6 @@ export function GroupLinkRemove(): JSX.Element { ); } -GroupLinkRemove.story = { - name: 'Group Link (Remove)', -}; - export function DescriptionRemove(): JSX.Element { return ( <> @@ -1552,10 +1489,6 @@ export function DescriptionRemove(): JSX.Element { ); } -DescriptionRemove.story = { - name: 'Description (Remove)', -}; - export function DescriptionChange(): JSX.Element { return ( <> @@ -1601,10 +1534,6 @@ export function DescriptionChange(): JSX.Element { ); } -DescriptionChange.story = { - name: 'Description (Change)', -}; - export function AnnouncementGroupChange(): JSX.Element { return ( <> @@ -1664,10 +1593,6 @@ export function AnnouncementGroupChange(): JSX.Element { ); } -AnnouncementGroupChange.story = { - name: 'Announcement Group (Change)', -}; - export function Summary(): JSX.Element { return ( <> diff --git a/ts/components/conversation/GroupV2PendingApprovalActions.stories.tsx b/ts/components/conversation/GroupV2PendingApprovalActions.stories.tsx index 1a4e0807e22d..caa5799ae450 100644 --- a/ts/components/conversation/GroupV2PendingApprovalActions.stories.tsx +++ b/ts/components/conversation/GroupV2PendingApprovalActions.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { PropsType as GroupV2PendingApprovalActionsPropsType } from './GroupV2PendingApprovalActions'; import { GroupV2PendingApprovalActions } from './GroupV2PendingApprovalActions'; import { setupI18n } from '../../util/setupI18n'; @@ -19,7 +19,7 @@ const createProps = (): GroupV2PendingApprovalActionsPropsType => ({ export default { title: 'Components/Conversation/GroupV2PendingApprovalActions', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/conversation/Image.stories.tsx b/ts/components/conversation/Image.stories.tsx index b5aa554c3b6e..5729d5971a73 100644 --- a/ts/components/conversation/Image.stories.tsx +++ b/ts/components/conversation/Image.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { pngUrl } from '../../storybook/Fixtures'; import type { Props } from './Image'; import { CurveType, Image } from './Image'; @@ -20,7 +19,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/Image', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ alt: overrideProps.alt || '', @@ -60,10 +59,6 @@ export function UrlWithHeightWidth(): JSX.Element { return ; } -UrlWithHeightWidth.story = { - name: 'URL with Height/Width', -}; - export function Caption(): JSX.Element { const defaultProps = createProps(); const props = { @@ -112,10 +107,6 @@ export function NoBorderOrBackground(): JSX.Element { ); } -NoBorderOrBackground.story = { - name: 'No Border or Background', -}; - export function Pending(): JSX.Element { const props = createProps({ attachment: fakeAttachment({ @@ -149,10 +140,6 @@ export function PendingWBlurhash(): JSX.Element { ); } -PendingWBlurhash.story = { - name: 'Pending w/blurhash', -}; - export function CurvedCorners(): JSX.Element { const props = createProps({ curveBottomLeft: CurveType.Normal, @@ -200,10 +187,6 @@ export function FullOverlayWithText(): JSX.Element { return ; } -FullOverlayWithText.story = { - name: 'Full Overlay with Text', -}; - export function Blurhash(): JSX.Element { const defaultProps = createProps(); const props = { @@ -229,10 +212,6 @@ export function UndefinedBlurHash(): JSX.Element { return ; } -UndefinedBlurHash.story = { - name: 'undefined blurHash', -}; - export function MissingImage(): JSX.Element { const defaultProps = createProps(); const props = { diff --git a/ts/components/conversation/ImageGrid.stories.tsx b/ts/components/conversation/ImageGrid.stories.tsx index 9f1eda49fb7c..5b890fee5048 100644 --- a/ts/components/conversation/ImageGrid.stories.tsx +++ b/ts/components/conversation/ImageGrid.stories.tsx @@ -2,10 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; -import { boolean, number } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { Props } from './ImageGrid'; import { ImageGrid } from './ImageGrid'; import { @@ -25,44 +23,14 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ImageGrid', -}; - -const createProps = (overrideProps: Partial = {}): Props => ({ - attachments: overrideProps.attachments || [ - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - ], - bottomOverlay: boolean('bottomOverlay', overrideProps.bottomOverlay || false), - direction: overrideProps.direction || 'incoming', - i18n, - isSticker: boolean('isSticker', overrideProps.isSticker || false), - onClick: action('onClick'), - onError: action('onError'), - stickerSize: number('stickerSize', overrideProps.stickerSize || 0), - tabIndex: number('tabIndex', overrideProps.tabIndex || 0), - withContentAbove: boolean( - 'withContentAbove', - overrideProps.withContentAbove || false - ), - withContentBelow: boolean( - 'withContentBelow', - overrideProps.withContentBelow || false - ), -}); - -export function OneImage(): JSX.Element { - const props = createProps(); - - return ; -} - -export function TwoImages(): JSX.Element { - const props = createProps({ + argTypes: { + bottomOverlay: { control: { type: 'boolean' } }, + isSticker: { control: { type: 'boolean' } }, + stickerSize: { control: { type: 'number' } }, + withContentAbove: { control: { type: 'boolean' } }, + withContentBelow: { control: { type: 'boolean' } }, + }, + args: { attachments: [ fakeAttachment({ contentType: IMAGE_PNG, @@ -71,267 +39,285 @@ export function TwoImages(): JSX.Element { url: pngUrl, width: 800, }), - fakeAttachment({ - contentType: IMAGE_JPEG, - fileName: 'tina-rolf-269345-unsplash.jpg', - height: 1680, - url: '/fixtures/tina-rolf-269345-unsplash.jpg', - width: 3000, - }), - ], - }); - - return ; -} - -export function ThreeImages(): JSX.Element { - const props = createProps({ - attachments: [ - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_JPEG, - fileName: 'tina-rolf-269345-unsplash.jpg', - height: 1680, - url: '/fixtures/tina-rolf-269345-unsplash.jpg', - width: 3000, - }), - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), ], - }); + bottomOverlay: false, + direction: 'incoming', + i18n, + isSticker: false, + onClick: action('onClick'), + onError: action('onError'), + stickerSize: 0, + tabIndex: 0, + withContentAbove: false, + withContentBelow: false, + }, +} satisfies Meta; - return ; +export function OneImage(args: Props): JSX.Element { + return ; } -export function FourImages(): JSX.Element { - const props = createProps({ - attachments: [ - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_JPEG, - fileName: 'tina-rolf-269345-unsplash.jpg', - height: 1680, - url: '/fixtures/tina-rolf-269345-unsplash.jpg', - width: 3000, - }), - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_JPEG, - fileName: 'tina-rolf-269345-unsplash.jpg', - height: 1680, - url: '/fixtures/tina-rolf-269345-unsplash.jpg', - width: 3000, - }), - ], - }); - - return ; -} - -export function FiveImages(): JSX.Element { - const props = createProps({ - attachments: [ - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_JPEG, - fileName: 'tina-rolf-269345-unsplash.jpg', - height: 1680, - url: '/fixtures/tina-rolf-269345-unsplash.jpg', - width: 3000, - }), - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_JPEG, - fileName: 'tina-rolf-269345-unsplash.jpg', - height: 1680, - url: '/fixtures/tina-rolf-269345-unsplash.jpg', - width: 3000, - }), - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - ], - }); - - return ; -} - -export const _6Images = (): JSX.Element => { - const props = createProps({ - attachments: [ - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_JPEG, - fileName: 'tina-rolf-269345-unsplash.jpg', - height: 1680, - url: '/fixtures/tina-rolf-269345-unsplash.jpg', - width: 3000, - }), - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_JPEG, - fileName: 'tina-rolf-269345-unsplash.jpg', - height: 1680, - url: '/fixtures/tina-rolf-269345-unsplash.jpg', - width: 3000, - }), - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - fakeAttachment({ - contentType: IMAGE_PNG, - fileName: 'sax.png', - height: 1200, - url: pngUrl, - width: 800, - }), - ], - }); - - return ; -}; - -_6Images.story = { - name: '6+ Images', -}; - -export function MixedContentTypes(): JSX.Element { - const props = createProps({ - attachments: [ - fakeAttachment({ - contentType: VIDEO_MP4, - fileName: 'pixabay-Soap-Bubble-7141.mp4', - height: 112, - screenshot: { - height: 112, - width: 112, - url: '/fixtures/kitten-4-112-112.jpg', +export function TwoImages(args: Props): JSX.Element { + return ( + ; + fileName: 'tina-rolf-269345-unsplash.jpg', + height: 1680, + url: '/fixtures/tina-rolf-269345-unsplash.jpg', + width: 3000, + }), + ]} + /> + ); } -export function Sticker(): JSX.Element { - const props = createProps({ - attachments: [ - fakeAttachment({ - contentType: IMAGE_WEBP, - fileName: 'sticker.webp', - height: 512, - url: squareStickerUrl, - width: 512, - }), - ], - isSticker: true, - stickerSize: 128, - }); - - return ; +export function ThreeImages(args: Props): JSX.Element { + return ( + + ); } -export function ContentAboveAndBelow(): JSX.Element { - const props = createProps({ - withContentAbove: true, - withContentBelow: true, - }); - - return ; +export function FourImages(args: Props): JSX.Element { + return ( + + ); } -ContentAboveAndBelow.story = { - name: 'Content Above and Below', +export function FiveImages(args: Props): JSX.Element { + return ( + + ); +} + +export const _6Images = (args: Props): JSX.Element => { + return ( + + ); }; -export function BottomOverlay(): JSX.Element { - const props = createProps({ - bottomOverlay: true, - }); - - return ; +export function MixedContentTypes(args: Props): JSX.Element { + return ( + + ); +} + +export function Sticker(args: Props): JSX.Element { + return ( + + ); +} + +export function ContentAboveAndBelow(args: Props): JSX.Element { + return ; +} + +export function BottomOverlay(args: Props): JSX.Element { + return ; } diff --git a/ts/components/conversation/LastSeenIndicator.stories.tsx b/ts/components/conversation/LastSeenIndicator.stories.tsx index 138d8c9189c0..e659a29d3aa6 100644 --- a/ts/components/conversation/LastSeenIndicator.stories.tsx +++ b/ts/components/conversation/LastSeenIndicator.stories.tsx @@ -2,9 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - -import { number } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { Props } from './LastSeenIndicator'; import { LastSeenIndicator } from './LastSeenIndicator'; import { setupI18n } from '../../util/setupI18n'; @@ -14,26 +12,19 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/LastSeenIndicator', -}; + argTypes: { + count: { control: { type: 'number' } }, + }, + args: { + i18n, + count: 1, + }, +} satisfies Meta; -const createProps = (overrideProps: Partial = {}): Props => ({ - count: number('count', overrideProps.count || 1), - i18n, -}); - -export function One(): JSX.Element { - const props = createProps(); - return ; +export function One(args: Props): JSX.Element { + return ; } -export function MoreThanOne(): JSX.Element { - const props = createProps({ - count: 5, - }); - - return ; +export function MoreThanOne(args: Props): JSX.Element { + return ; } - -MoreThanOne.story = { - name: 'More than One', -}; diff --git a/ts/components/conversation/Linkify.stories.tsx b/ts/components/conversation/Linkify.stories.tsx index 23140ac3869d..46b7eae8a856 100644 --- a/ts/components/conversation/Linkify.stories.tsx +++ b/ts/components/conversation/Linkify.stories.tsx @@ -2,13 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import type { Props } from './Linkify'; import { Linkify } from './Linkify'; export default { title: 'Components/Conversation/Linkify', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ renderNonLink: overrideProps.renderNonLink, @@ -31,10 +31,6 @@ export function LinksWithText(): JSX.Element { return ; } -LinksWithText.story = { - name: 'Links with Text', -}; - export function LinksWithEmojiWithoutSpace(): JSX.Element { const props = createProps({ text: '👍https://www.signal.org😎', @@ -43,10 +39,6 @@ export function LinksWithEmojiWithoutSpace(): JSX.Element { return ; } -LinksWithEmojiWithoutSpace.story = { - name: 'Links with Emoji without space', -}; - export function LinksWithEmojiAndText(): JSX.Element { const props = createProps({ text: 'https://example.com ⚠️ 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ https://example.com', @@ -55,10 +47,6 @@ export function LinksWithEmojiAndText(): JSX.Element { return ; } -LinksWithEmojiAndText.story = { - name: 'Links with Emoji and Text', -}; - export function NoLink(): JSX.Element { const props = createProps({ text: 'I am fond of cats', @@ -83,10 +71,6 @@ export function MissingProtocols(): JSX.Element { return ; } -MissingProtocols.story = { - name: 'Missing protocols', -}; - export function CustomTextRender(): JSX.Element { const props = createProps({ text: 'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!', diff --git a/ts/components/conversation/MandatoryProfileSharingActions.stories.tsx b/ts/components/conversation/MandatoryProfileSharingActions.stories.tsx index 8ae788209d0c..e0d355e13ddd 100644 --- a/ts/components/conversation/MandatoryProfileSharingActions.stories.tsx +++ b/ts/components/conversation/MandatoryProfileSharingActions.stories.tsx @@ -2,48 +2,52 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - -import type { Props as MandatoryProfileSharingActionsProps } from './MandatoryProfileSharingActions'; +import type { Meta } from '@storybook/react'; +import type { Props } from './MandatoryProfileSharingActions'; import { MandatoryProfileSharingActions } from './MandatoryProfileSharingActions'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; const i18n = setupI18n('en', enMessages); -const getBaseProps = ( - isGroup = false -): MandatoryProfileSharingActionsProps => ({ - conversationId: '123', - i18n, - conversationType: isGroup ? 'group' : 'direct', - firstName: text('firstName', 'Cayce'), - title: isGroup - ? text('title', 'NYC Rock Climbers') - : text('title', 'Cayce Bollard'), - acceptConversation: action('acceptConversation'), - blockAndReportSpam: action('blockAndReportSpam'), - blockConversation: action('blockConversation'), - deleteConversation: action('deleteConversation'), -}); - export default { title: 'Components/Conversation/MandatoryProfileSharingActions', -}; + argTypes: { + conversationType: { + control: { + type: 'select', + options: ['direct', 'group'], + }, + }, + firstName: { control: { type: 'text' } }, + title: { control: { type: 'text' } }, + }, + args: { + conversationId: '123', + i18n, + conversationType: 'direct', + firstName: 'Cayce', + title: 'Cayce Bollard', + acceptConversation: action('acceptConversation'), + blockAndReportSpam: action('blockAndReportSpam'), + blockConversation: action('blockConversation'), + deleteConversation: action('deleteConversation'), + }, +} satisfies Meta; -export function Direct(): JSX.Element { +export function Direct(args: Props): JSX.Element { return (
- +
); } -export function Group(): JSX.Element { +export function Group(args: Props): JSX.Element { return (
- +
); } diff --git a/ts/components/conversation/MessageBody.stories.tsx b/ts/components/conversation/MessageBody.stories.tsx index 83d76b1ce261..be793090d7ec 100644 --- a/ts/components/conversation/MessageBody.stories.tsx +++ b/ts/components/conversation/MessageBody.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { Props } from './MessageBody'; import { MessageBody } from './MessageBody'; import { setupI18n } from '../../util/setupI18n'; @@ -28,7 +28,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/MessageBody', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ bodyRanges: overrideProps.bodyRanges, @@ -107,10 +107,6 @@ export function JumbomojiDisabledByText(): JSX.Element { return ; } -JumbomojiDisabledByText.story = { - name: 'Jumbomoji Disabled by Text', -}; - export function TextPending(): JSX.Element { const props = createProps({ text: 'Check out https://www.signal.org', @@ -139,10 +135,6 @@ export function Mention(): JSX.Element { return ; } -Mention.story = { - name: '@Mention', -}; - export function MultipleMentions(): JSX.Element { const props = createProps({ // These are intentionally in a mixed order to test how we deal with that @@ -181,10 +173,6 @@ export function MultipleMentions(): JSX.Element { ); } -MultipleMentions.story = { - name: 'Multiple @Mentions', -}; - export function ComplexMessageBody(): JSX.Element { const props = createProps({ bodyRanges: [ @@ -224,10 +212,6 @@ export function ComplexMessageBody(): JSX.Element { ); } -ComplexMessageBody.story = { - name: 'Complex MessageBody', -}; - export function FormattingBasic(): JSX.Element { const [isSpoilerExpanded, setIsSpoilerExpanded] = React.useState({}); diff --git a/ts/components/conversation/MessageBodyReadMore.stories.tsx b/ts/components/conversation/MessageBodyReadMore.stories.tsx index 24add4f29b5d..27e3f8f32e51 100644 --- a/ts/components/conversation/MessageBodyReadMore.stories.tsx +++ b/ts/components/conversation/MessageBodyReadMore.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState } from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { Props } from './MessageBodyReadMore'; import { MessageBodyReadMore } from './MessageBodyReadMore'; import { setupI18n } from '../../util/setupI18n'; @@ -18,7 +17,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/MessageBodyReadMore', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ bodyRanges: overrideProps.bodyRanges, @@ -68,20 +67,12 @@ export function LongText100More(): JSX.Element { ); } -LongText100More.story = { - name: 'Long text + 100 more', -}; - export function LotsOfCakeWithSomeCherriesOnTop(): JSX.Element { return ( ); } -LotsOfCakeWithSomeCherriesOnTop.story = { - name: 'Lots of cake with some cherries on top', -}; - export function LeafyNotBuffered(): JSX.Element { return ; } @@ -154,10 +145,6 @@ export function LongTextMostlySpoiler(): JSX.Element { ); } -LeafyNotBuffered.story = { - name: 'Leafy not buffered', -}; - export function Links(): JSX.Element { return ( ; } -ExcessiveAmountsOfCake.story = { - name: 'Excessive amounts of cake', -}; - export function LongText(): JSX.Element { return ( ); } - -LongText.story = { - name: 'Long text', -}; diff --git a/ts/components/conversation/MessageDetail.stories.tsx b/ts/components/conversation/MessageDetail.stories.tsx index 13d9025914e9..6d9f01bff551 100644 --- a/ts/components/conversation/MessageDetail.stories.tsx +++ b/ts/components/conversation/MessageDetail.stories.tsx @@ -2,10 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; -import { number } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { PropsData as MessageDataPropsType } from './Message'; import { TextDirection } from './Message'; import type { Props } from './MessageDetail'; @@ -20,10 +18,6 @@ import { ThemeType } from '../../types/Util'; const i18n = setupI18n('en', enMessages); -export default { - title: 'Components/Conversation/MessageDetail', -}; - const defaultMessage: MessageDataPropsType = { author: getDefaultConversation({ id: 'some-id', @@ -51,206 +45,224 @@ const defaultMessage: MessageDataPropsType = { timestamp: Date.now(), }; -const createProps = (overrideProps: Partial = {}): Props => ({ - contacts: overrideProps.contacts || [ - { - ...getDefaultConversation({ - title: 'Just Max', - }), - isOutgoingKeyError: false, - isUnidentifiedDelivery: false, - status: SendStatus.Delivered, - }, - ], - errors: overrideProps.errors || [], - message: overrideProps.message || defaultMessage, - receivedAt: number('receivedAt', overrideProps.receivedAt || Date.now()), - sentAt: number('sentAt', overrideProps.sentAt || Date.now()), - - getPreferredBadge: () => getFakeBadge(), - i18n, - platform: 'darwin', - interactionMode: 'keyboard', - theme: ThemeType.light, - - toggleSafetyNumberModal: action('toggleSafetyNumberModal'), - - checkForAccount: action('checkForAccount'), - clearTargetedMessage: action('clearTargetedMessage'), - showLightboxForViewOnceMedia: action('showLightboxForViewOnceMedia'), - doubleCheckMissingQuoteReference: action('doubleCheckMissingQuoteReference'), - kickOffAttachmentDownload: action('kickOffAttachmentDownload'), - markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'), - messageExpanded: action('messageExpanded'), - showConversation: action('showConversation'), - openGiftBadge: action('openGiftBadge'), - renderAudioAttachment: () =>
*AudioAttachment*
, - saveAttachment: action('saveAttachment'), - showSpoiler: action('showSpoiler'), - retryMessageSend: action('retryMessageSend'), - pushPanelForConversation: action('pushPanelForConversation'), - showContactModal: action('showContactModal'), - showExpiredIncomingTapToViewToast: action( - 'showExpiredIncomingTapToViewToast' - ), - showExpiredOutgoingTapToViewToast: action( - 'showExpiredOutgoingTapToViewToast' - ), - showLightbox: action('showLightbox'), - startConversation: action('startConversation'), - viewStory: action('viewStory'), -}); - -export function DeliveredIncoming(): JSX.Element { - const props = createProps({ +export default { + title: 'Components/Conversation/MessageDetail', + argTypes: { + message: { control: { type: 'text' } }, + receivedAt: { control: { type: 'number' } }, + sentAt: { control: { type: 'number' } }, + }, + args: { contacts: [ { ...getDefaultConversation({ - color: 'forest', - title: 'Max', - }), - status: undefined, - isOutgoingKeyError: false, - isUnidentifiedDelivery: false, - }, - ], - }); - return ; -} - -export function DeliveredOutgoing(): JSX.Element { - const props = createProps({ - message: { - ...defaultMessage, - direction: 'outgoing', - text: 'A message to Max', - }, - }); - return ; -} - -export function MessageStatuses(): JSX.Element { - const props = createProps({ - contacts: [ - { - ...getDefaultConversation({ - title: 'Max', - }), - isOutgoingKeyError: false, - isUnidentifiedDelivery: false, - status: SendStatus.Sent, - }, - { - ...getDefaultConversation({ - title: 'Sally', - }), - isOutgoingKeyError: false, - isUnidentifiedDelivery: false, - status: SendStatus.Pending, - }, - { - ...getDefaultConversation({ - title: 'Terry', - }), - isOutgoingKeyError: false, - isUnidentifiedDelivery: false, - status: SendStatus.Failed, - }, - { - ...getDefaultConversation({ - title: 'Theo', + title: 'Just Max', }), isOutgoingKeyError: false, isUnidentifiedDelivery: false, status: SendStatus.Delivered, }, - { - ...getDefaultConversation({ - title: 'Nikki', - }), - isOutgoingKeyError: false, - isUnidentifiedDelivery: false, - status: SendStatus.Read, - }, ], - message: { - ...defaultMessage, - conversationType: 'group', - text: 'A message to you all!', - }, - }); - return ; + errors: [], + message: defaultMessage, + receivedAt: Date.now(), + sentAt: Date.now(), + + getPreferredBadge: () => getFakeBadge(), + i18n, + platform: 'darwin', + interactionMode: 'keyboard', + theme: ThemeType.light, + + toggleSafetyNumberModal: action('toggleSafetyNumberModal'), + + checkForAccount: action('checkForAccount'), + clearTargetedMessage: action('clearTargetedMessage'), + showLightboxForViewOnceMedia: action('showLightboxForViewOnceMedia'), + doubleCheckMissingQuoteReference: action( + 'doubleCheckMissingQuoteReference' + ), + kickOffAttachmentDownload: action('kickOffAttachmentDownload'), + markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'), + messageExpanded: action('messageExpanded'), + showConversation: action('showConversation'), + openGiftBadge: action('openGiftBadge'), + renderAudioAttachment: () =>
AudioAttachment
, + saveAttachment: action('saveAttachment'), + showSpoiler: action('showSpoiler'), + retryMessageSend: action('retryMessageSend'), + pushPanelForConversation: action('pushPanelForConversation'), + showContactModal: action('showContactModal'), + showExpiredIncomingTapToViewToast: action( + 'showExpiredIncomingTapToViewToast' + ), + showExpiredOutgoingTapToViewToast: action( + 'showExpiredOutgoingTapToViewToast' + ), + showLightbox: action('showLightbox'), + startConversation: action('startConversation'), + viewStory: action('viewStory'), + }, +} satisfies Meta; + +export function DeliveredIncoming(args: Props): JSX.Element { + return ( + + ); } -export function NotDelivered(): JSX.Element { - const props = createProps({ - message: { - ...defaultMessage, - direction: 'outgoing', - text: 'A message to Max', - }, - }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - props.receivedAt = undefined as any; - - return ; +export function DeliveredOutgoing(args: Props): JSX.Element { + return ( + + ); } -export function NoContacts(): JSX.Element { - const props = createProps({ - contacts: [], - message: { - ...defaultMessage, - direction: 'outgoing', - text: 'Is anybody there?', - }, - }); - return ; +export function MessageStatuses(args: Props): JSX.Element { + return ( + + ); } -export function AllErrors(): JSX.Element { - const props = createProps({ - errors: [ - { - name: 'Another Error', - message: 'Wow, that went bad.', - }, - ], - message: { - ...defaultMessage, - }, - contacts: [ - { - ...getDefaultConversation({ - title: 'Max', - }), - isOutgoingKeyError: true, - isUnidentifiedDelivery: false, - status: SendStatus.Failed, - }, - { - ...getDefaultConversation({ - title: 'Sally', - }), - errors: [ - { - name: 'Big Error', - message: 'Stuff happened, in a bad way.', - }, - ], - isOutgoingKeyError: false, - isUnidentifiedDelivery: true, - status: SendStatus.Failed, - }, - { - ...getDefaultConversation({ - title: 'Terry', - }), - isOutgoingKeyError: true, - isUnidentifiedDelivery: true, - status: SendStatus.Failed, - }, - ], - }); - return ; +export function NotDelivered(args: Props): JSX.Element { + return ( + + ); +} + +export function NoContacts(args: Props): JSX.Element { + return ( + + ); +} + +export function AllErrors(args: Props): JSX.Element { + return ( + + ); } diff --git a/ts/components/conversation/MessageRequestActions.stories.tsx b/ts/components/conversation/MessageRequestActions.stories.tsx index 81586efbffcc..a4411f9084fe 100644 --- a/ts/components/conversation/MessageRequestActions.stories.tsx +++ b/ts/components/conversation/MessageRequestActions.stories.tsx @@ -2,70 +2,61 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - -import type { Props as MessageRequestActionsProps } from './MessageRequestActions'; +import type { Meta } from '@storybook/react'; +import type { Props } from './MessageRequestActions'; import { MessageRequestActions } from './MessageRequestActions'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; const i18n = setupI18n('en', enMessages); -const getBaseProps = (isGroup = false): MessageRequestActionsProps => ({ - conversationId: '123', - i18n, - conversationType: isGroup ? 'group' : 'direct', - firstName: text('firstName', 'Cayce'), - title: isGroup - ? text('title', 'NYC Rock Climbers') - : text('title', 'Cayce Bollard'), - acceptConversation: action('acceptConversation'), - blockAndReportSpam: action('blockAndReportSpam'), - blockConversation: action('blockConversation'), - deleteConversation: action('deleteConversation'), -}); - export default { title: 'Components/Conversation/MessageRequestActions', -}; + argTypes: { + conversationType: { + control: { + type: 'select', + options: ['direct', 'group'], + }, + }, + firstName: { control: { type: 'text' } }, + title: { control: { type: 'text' } }, + }, + args: { + conversationId: '123', + i18n, + conversationType: 'direct', + firstName: 'Cayce', + title: 'Cayce Bollard', + acceptConversation: action('acceptConversation'), + blockAndReportSpam: action('blockAndReportSpam'), + blockConversation: action('blockConversation'), + deleteConversation: action('deleteConversation'), + }, + decorators: [ + (Story: React.ComponentType): JSX.Element => { + return ( +
+ +
+ ); + }, + ], +} satisfies Meta; -export function Direct(): JSX.Element { - return ( -
- -
- ); +export function Direct(args: Props): JSX.Element { + return ; } -export function DirectBlocked(): JSX.Element { - return ( -
- -
- ); +export function DirectBlocked(args: Props): JSX.Element { + return ; } -DirectBlocked.story = { - name: 'Direct (Blocked)', -}; - -export function Group(): JSX.Element { - return ( -
- -
- ); +export function Group(args: Props): JSX.Element { + return ; } -export function GroupBlocked(): JSX.Element { - return ( -
- -
- ); +export function GroupBlocked(args: Props): JSX.Element { + return ; } - -GroupBlocked.story = { - name: 'Group (Blocked)', -}; diff --git a/ts/components/conversation/MessageTimestamp.stories.tsx b/ts/components/conversation/MessageTimestamp.stories.tsx index a161a7a7fc7d..eb2f0a735242 100644 --- a/ts/components/conversation/MessageTimestamp.stories.tsx +++ b/ts/components/conversation/MessageTimestamp.stories.tsx @@ -2,8 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { boolean, date, select, text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { Props } from './MessageTimestamp'; @@ -11,10 +10,6 @@ import { MessageTimestamp } from './MessageTimestamp'; const i18n = setupI18n('en', enMessages); -export default { - title: 'Components/Conversation/MessageTimestamp', -}; - const { now } = Date; const seconds = (n: number) => n * 1000; const minutes = (n: number) => 60 * seconds(n); @@ -40,51 +35,57 @@ const times = (): Array<[string, number]> => [ ['366d ago', now() - days(366)], ]; -const createProps = (overrideProps: Partial = {}): Props => ({ - i18n, - timestamp: overrideProps.timestamp || Date.now(), - module: text('module', ''), - withImageNoCaption: boolean('withImageNoCaption', false), - withSticker: boolean('withSticker', false), - withTapToViewExpired: boolean('withTapToViewExpired', false), - direction: - select( - 'direction', - { none: '', incoming: 'incoming', outgoing: 'outgoing' }, - '' - ) || undefined, -}); +export default { + title: 'Components/Conversation/MessageTimestamp', + argTypes: { + timestamp: { control: { type: 'number' } }, + module: { control: { type: 'text' } }, + withImageNoCaption: { control: { type: 'boolean' } }, + withSticker: { control: { type: 'boolean' } }, + withTapToViewExpired: { control: { type: 'boolean' } }, + direction: { + control: { + type: 'select', + options: ['', 'incoming', 'outgoing'], + }, + }, + }, + args: { + i18n, + timestamp: Date.now(), + module: '', + withImageNoCaption: false, + withSticker: false, + withTapToViewExpired: false, + direction: undefined, + }, +} satisfies Meta; -const createTable = (overrideProps: Partial = {}) => ( - - - - - - - {times().map(([description, timestamp]) => ( - - - +export function Normal(args: Props): JSX.Element { + return ( +
DescriptionTimestamp
{description} - -
+ + + + - ))} - -
DescriptionTimestamp
-); - -export const Normal = (): JSX.Element => { - return createTable(); -}; - -export function Knobs(): JSX.Element { - const props = createProps({ - timestamp: date('timestamp', new Date()), - }); - - return ; + {times().map(([description, timestamp]) => ( + + {description} + + + + + ))} + + + ); +} + +export function Knobs(args: Props): JSX.Element { + return ; } diff --git a/ts/components/conversation/ProfileChangeNotification.stories.tsx b/ts/components/conversation/ProfileChangeNotification.stories.tsx index 49bb4b4e01d6..ab32816be135 100644 --- a/ts/components/conversation/ProfileChangeNotification.stories.tsx +++ b/ts/components/conversation/ProfileChangeNotification.stories.tsx @@ -2,17 +2,18 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import type { PropsType } from './ProfileChangeNotification'; import { ProfileChangeNotification } from './ProfileChangeNotification'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ProfileChangeNotification', -}; +} satisfies Meta; export function FromContact(): JSX.Element { return ( @@ -33,10 +34,6 @@ export function FromContact(): JSX.Element { ); } -FromContact.story = { - name: 'From contact', -}; - export function FromNonContact(): JSX.Element { return ( ); } - -FromContactWithLongNamesBeforeAndAfter.story = { - name: 'From contact with long names before and after', -}; diff --git a/ts/components/conversation/Quote.stories.tsx b/ts/components/conversation/Quote.stories.tsx index 52ed0d697de5..b359b19c6df1 100644 --- a/ts/components/conversation/Quote.stories.tsx +++ b/ts/components/conversation/Quote.stories.tsx @@ -1,7 +1,7 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import * as React from 'react'; import { action } from '@storybook/addon-actions'; @@ -34,49 +34,40 @@ export default { component: Quote, title: 'Components/Conversation/Quote', argTypes: { - authorTitle: { - defaultValue: 'Default Sender', - }, - conversationColor: { - defaultValue: 'forest', - }, - doubleCheckMissingQuoteReference: { action: true }, - i18n: { - defaultValue: i18n, - }, - platform: { - defautlValue: 'darwin', - }, isFromMe: { control: { type: 'checkbox' }, - defaultValue: false, }, isGiftBadge: { control: { type: 'checkbox' }, - defaultValue: false, }, isIncoming: { control: { type: 'checkbox' }, - defaultValue: false, }, isViewOnce: { control: { type: 'checkbox' }, - defaultValue: false, - }, - onClick: { action: true }, - onClose: { action: true }, - rawAttachment: { - defaultValue: undefined, }, referencedMessageNotFound: { control: { type: 'checkbox' }, - defaultValue: false, - }, - text: { - defaultValue: 'A sample message from a pal', }, }, -} as Meta; + args: { + authorTitle: 'Default Sender', + conversationColor: 'forest', + doubleCheckMissingQuoteReference: action( + 'doubleCheckMissingQuoteReference' + ), + i18n, + isFromMe: false, + isGiftBadge: false, + isIncoming: false, + isViewOnce: false, + onClick: action('onClick'), + onClose: action('onClose'), + rawAttachment: undefined, + referencedMessageNotFound: false, + text: 'A sample message from a pal', + }, +} satisfies Meta; const defaultMessageProps: TimelineMessagesProps = { author: getDefaultConversation({ @@ -197,42 +188,30 @@ const renderInMessage = ({ }; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; -const TemplateInMessage: Story = args => renderInMessage(args); +const Template: StoryFn = args => ; +const TemplateInMessage: StoryFn = args => renderInMessage(args); export const OutgoingByAnotherAuthor = Template.bind({}); OutgoingByAnotherAuthor.args = { authorTitle: getDefaultConversation().title, }; -OutgoingByAnotherAuthor.story = { - name: 'Outgoing by Another Author', -}; export const OutgoingByMe = Template.bind({}); OutgoingByMe.args = { isFromMe: true, }; -OutgoingByMe.story = { - name: 'Outgoing by Me', -}; export const IncomingByAnotherAuthor = Template.bind({}); IncomingByAnotherAuthor.args = { authorTitle: getDefaultConversation().title, isIncoming: true, }; -IncomingByAnotherAuthor.story = { - name: 'Incoming by Another Author', -}; export const IncomingByMe = Template.bind({}); IncomingByMe.args = { isFromMe: true, isIncoming: true, }; -IncomingByMe.story = { - name: 'Incoming by Me', -}; export function IncomingOutgoingColors(args: Props): JSX.Element { return ( @@ -244,9 +223,6 @@ export function IncomingOutgoingColors(args: Props): JSX.Element { ); } IncomingOutgoingColors.args = {}; -IncomingOutgoingColors.story = { - name: 'Incoming/Outgoing Colors', -}; export const ImageOnly = Template.bind({}); ImageOnly.args = { @@ -289,9 +265,6 @@ ImageAttachmentNoThumbnail.args = { isVoiceMessage: false, }, }; -ImageAttachmentNoThumbnail.story = { - name: 'Image Attachment w/o Thumbnail', -}; export const ImageTapToView = Template.bind({}); ImageTapToView.args = { @@ -303,9 +276,6 @@ ImageTapToView.args = { isVoiceMessage: false, }, }; -ImageTapToView.story = { - name: 'Image Tap-to-View', -}; export const VideoOnly = Template.bind({}); VideoOnly.args = { @@ -348,9 +318,6 @@ VideoAttachmentNoThumbnail.args = { isVoiceMessage: false, }, }; -VideoAttachmentNoThumbnail.story = { - name: 'Video Attachment w/o Thumbnail', -}; export const VideoTapToView = Template.bind({}); VideoTapToView.args = { @@ -362,9 +329,6 @@ VideoTapToView.args = { isVoiceMessage: false, }, }; -VideoTapToView.story = { - name: 'Video Tap-to-View', -}; export const GiftBadge = TemplateInMessage.bind({}); GiftBadge.args = { @@ -430,9 +394,6 @@ MediaTapToView.args = { isVoiceMessage: false, }, }; -MediaTapToView.story = { - name: 'Media Tap-to-View', -}; export const OtherFileAttachment = Template.bind({}); OtherFileAttachment.args = { @@ -451,9 +412,6 @@ LongMessageAttachmentShouldBeHidden.args = { isVoiceMessage: false, }, }; -LongMessageAttachmentShouldBeHidden.story = { - name: 'Long message attachment (should be hidden)', -}; export const NoCloseButton = Template.bind({}); NoCloseButton.args = { @@ -469,27 +427,18 @@ export const MissingTextAttachment = Template.bind({}); MissingTextAttachment.args = { text: undefined, }; -MissingTextAttachment.story = { - name: 'Missing Text & Attachment', -}; export const MentionOutgoingAnotherAuthor = Template.bind({}); MentionOutgoingAnotherAuthor.args = { authorTitle: 'Tony Stark', text: '@Captain America Lunch later?', }; -MentionOutgoingAnotherAuthor.story = { - name: '@mention + outgoing + another author', -}; export const MentionOutgoingMe = Template.bind({}); MentionOutgoingMe.args = { isFromMe: true, text: '@Captain America Lunch later?', }; -MentionOutgoingMe.story = { - name: '@mention + outgoing + me', -}; export const MentionIncomingAnotherAuthor = Template.bind({}); MentionIncomingAnotherAuthor.args = { @@ -497,9 +446,6 @@ MentionIncomingAnotherAuthor.args = { isIncoming: true, text: '@Tony Stark sure', }; -MentionIncomingAnotherAuthor.story = { - name: '@mention + incoming + another author', -}; export const MentionIncomingMe = Template.bind({}); MentionIncomingMe.args = { @@ -507,9 +453,6 @@ MentionIncomingMe.args = { isIncoming: true, text: '@Tony Stark sure', }; -MentionIncomingMe.story = { - name: '@mention + incoming + me', -}; export function CustomColor(args: Props): JSX.Element { return ( @@ -551,9 +494,6 @@ IsStoryReply.args = { isVoiceMessage: false, }, }; -IsStoryReply.story = { - name: 'isStoryReply', -}; export const IsStoryReplyEmoji = Template.bind({}); IsStoryReplyEmoji.args = { @@ -575,9 +515,6 @@ IsStoryReplyEmoji.args = { }, reactionEmoji: '🏋️', }; -IsStoryReplyEmoji.story = { - name: 'isStoryReply emoji', -}; export const Payment = Template.bind({}); Payment.args = { diff --git a/ts/components/conversation/ReactionPicker.stories.tsx b/ts/components/conversation/ReactionPicker.stories.tsx index bb62616fcd55..fbd6992513fa 100644 --- a/ts/components/conversation/ReactionPicker.stories.tsx +++ b/ts/components/conversation/ReactionPicker.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { Props as ReactionPickerProps } from './ReactionPicker'; @@ -33,7 +32,7 @@ const renderEmojiPicker: ReactionPickerProps['renderEmojiPicker'] = ({ export default { title: 'Components/Conversation/ReactionPicker', -}; +} satisfies Meta; export function Base(): JSX.Element { return ( diff --git a/ts/components/conversation/ReactionViewer.stories.tsx b/ts/components/conversation/ReactionViewer.stories.tsx index 4193a3a9a9e2..2a78ddfc13c2 100644 --- a/ts/components/conversation/ReactionViewer.stories.tsx +++ b/ts/components/conversation/ReactionViewer.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { Props } from './ReactionViewer'; import { ReactionViewer } from './ReactionViewer'; import { setupI18n } from '../../util/setupI18n'; @@ -16,7 +15,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ReactionViewer', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ getPreferredBadge: () => undefined, diff --git a/ts/components/conversation/ResetSessionNotification.stories.tsx b/ts/components/conversation/ResetSessionNotification.stories.tsx index ed81ac18895d..7b02146b5050 100644 --- a/ts/components/conversation/ResetSessionNotification.stories.tsx +++ b/ts/components/conversation/ResetSessionNotification.stories.tsx @@ -2,7 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; +import type { Props } from './ResetSessionNotification'; import { ResetSessionNotification } from './ResetSessionNotification'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; @@ -11,7 +12,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ResetSessionNotification', -}; +} satisfies Meta; export function Notification(): JSX.Element { return ; diff --git a/ts/components/conversation/SafetyNumberNotification.stories.tsx b/ts/components/conversation/SafetyNumberNotification.stories.tsx index 8cc15a15f96a..20c91cf88d30 100644 --- a/ts/components/conversation/SafetyNumberNotification.stories.tsx +++ b/ts/components/conversation/SafetyNumberNotification.stories.tsx @@ -3,8 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; -import { boolean, text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { ContactType, Props } from './SafetyNumberNotification'; @@ -14,53 +13,54 @@ const i18n = setupI18n('en', enMessages); const createContact = (props: Partial): ContactType => ({ id: '', - title: text('contact title', props.title || ''), -}); - -const createProps = (overrideProps: Partial = {}): Props => ({ - i18n, - contact: overrideProps.contact || ({} as ContactType), - isGroup: boolean('isGroup', overrideProps.isGroup || false), - toggleSafetyNumberModal: action('toggleSafetyNumberModal'), + title: props.title ?? '', }); export default { title: 'Components/Conversation/SafetyNumberNotification', -}; - -export function GroupConversation(): JSX.Element { - const props = createProps({ - isGroup: true, - contact: createContact({ - title: 'Mr. Fire', - }), - }); - - return ; -} - -export function DirectConversation(): JSX.Element { - const props = createProps({ + argTypes: { + isGroup: { control: { type: 'boolean' } }, + }, + args: { + i18n, + contact: {} as ContactType, isGroup: false, - contact: createContact({ - title: 'Mr. Fire', - }), - }); + toggleSafetyNumberModal: action('toggleSafetyNumberModal'), + }, +} satisfies Meta; - return ; +export function GroupConversation(args: Props): JSX.Element { + return ( + + ); } -export function LongNameInGroup(): JSX.Element { - const props = createProps({ - isGroup: true, - contact: createContact({ - title: '🐈‍⬛🍕🎂'.repeat(50), - }), - }); - - return ; +export function DirectConversation(args: Props): JSX.Element { + return ( + + ); } -LongNameInGroup.story = { - name: 'Long name in group', -}; +export function LongNameInGroup(args: Props): JSX.Element { + return ( + + ); +} diff --git a/ts/components/conversation/ScrollDownButton.stories.tsx b/ts/components/conversation/ScrollDownButton.stories.tsx index b9bafb6e956c..d7574a7b109a 100644 --- a/ts/components/conversation/ScrollDownButton.stories.tsx +++ b/ts/components/conversation/ScrollDownButton.stories.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { setupI18n } from '../../util/setupI18n'; @@ -35,10 +35,10 @@ export default { }, }, }, -} as Meta; +} satisfies Meta; // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ( +const Template: StoryFn = args => ( ); @@ -46,14 +46,8 @@ export const UnreadMessages = Template.bind({}); UnreadMessages.args = createProps({ variant: ScrollDownButtonVariant.UNREAD_MESSAGES, }); -UnreadMessages.story = { - name: 'UnreadMessages', -}; export const UnreadMentions = Template.bind({}); UnreadMentions.args = createProps({ variant: ScrollDownButtonVariant.UNREAD_MENTIONS, }); -UnreadMentions.story = { - name: 'UnreadMentions', -}; diff --git a/ts/components/conversation/StagedGenericAttachment.stories.tsx b/ts/components/conversation/StagedGenericAttachment.stories.tsx index 01e9ec97b10c..5b17fa317178 100644 --- a/ts/components/conversation/StagedGenericAttachment.stories.tsx +++ b/ts/components/conversation/StagedGenericAttachment.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { AttachmentType } from '../../types/Attachment'; import { stringToMIMEType } from '../../types/MIME'; import { setupI18n } from '../../util/setupI18n'; @@ -16,51 +15,51 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/StagedGenericAttachment', -}; - -const createProps = (overrideProps: Partial = {}): Props => ({ - attachment: overrideProps.attachment || ({} as AttachmentType), - i18n, - onClose: action('onClose'), -}); + argTypes: {}, + args: { + attachment: { + contentType: stringToMIMEType(''), + fileName: '', + url: '', + size: 14243, + }, + i18n, + onClose: action('onClose'), + }, +} satisfies Meta; const createAttachment = ( props: Partial = {} ): AttachmentType => ({ - contentType: stringToMIMEType( - text('attachment contentType', props.contentType || '') - ), - fileName: text('attachment fileName', props.fileName || ''), + contentType: stringToMIMEType(props.contentType ?? ''), + fileName: props.fileName ?? '', url: '', size: 14243, }); -export function TextFile(): JSX.Element { +export function TextFile(args: Props): JSX.Element { const attachment = createAttachment({ contentType: stringToMIMEType('text/plain'), fileName: 'manifesto.txt', }); - const props = createProps({ attachment }); - return ; + return ; } -export function LongName(): JSX.Element { +export function LongName(args: Props): JSX.Element { const attachment = createAttachment({ contentType: stringToMIMEType('text/plain'), fileName: 'this-is-my-very-important-manifesto-you-must-read-it.txt', }); - const props = createProps({ attachment }); - return ; + return ; } -export function LongExtension(): JSX.Element { +export function LongExtension(args: Props): JSX.Element { const attachment = createAttachment({ contentType: stringToMIMEType('text/plain'), fileName: 'manifesto.reallylongtxt', }); - const props = createProps({ attachment }); - return ; + return ; } diff --git a/ts/components/conversation/StagedLinkPreview.stories.tsx b/ts/components/conversation/StagedLinkPreview.stories.tsx index 7b7966fd8dfd..093bcbccd621 100644 --- a/ts/components/conversation/StagedLinkPreview.stories.tsx +++ b/ts/components/conversation/StagedLinkPreview.stories.tsx @@ -1,7 +1,7 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import * as React from 'react'; import { action } from '@storybook/addon-actions'; @@ -22,7 +22,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/StagedLinkPreview', component: StagedLinkPreview, -} as Meta; +} satisfies Meta; const getDefaultProps = (): Props => ({ date: Date.now(), @@ -35,7 +35,7 @@ const getDefaultProps = (): Props => ({ }); // eslint-disable-next-line react/function-component-definition -const Template: Story = args => ; +const Template: StoryFn = args => ; export const Loading = Template.bind({}); Loading.args = { @@ -44,6 +44,9 @@ Loading.args = { }; export const NoImage = Template.bind({}); +NoImage.args = { + ...getDefaultProps(), +}; export const Image = Template.bind({}); Image.args = { @@ -65,18 +68,12 @@ ImageNoTitleOrDescription.args = { contentType: IMAGE_JPEG, }), }; -ImageNoTitleOrDescription.story = { - name: 'Image, No Title Or Description', -}; export const NoImageLongTitleWithDescription = Template.bind({}); NoImageLongTitleWithDescription.args = { ...getDefaultProps(), title: LONG_TITLE, }; -NoImageLongTitleWithDescription.story = { - name: 'No Image, Long Title With Description', -}; export const NoImageLongTitleWithoutDescription = Template.bind({}); NoImageLongTitleWithoutDescription.args = { @@ -84,9 +81,6 @@ NoImageLongTitleWithoutDescription.args = { title: LONG_TITLE, description: '', }; -NoImageLongTitleWithoutDescription.story = { - name: 'No Image, Long Title Without Description', -}; export const ImageLongTitleWithoutDescription = Template.bind({}); ImageLongTitleWithoutDescription.args = { @@ -97,9 +91,6 @@ ImageLongTitleWithoutDescription.args = { contentType: IMAGE_JPEG, }), }; -ImageLongTitleWithoutDescription.story = { - name: 'Image, Long Title Without Description', -}; export const ImageLongTitleAndDescription = Template.bind({}); ImageLongTitleAndDescription.args = { @@ -111,9 +102,6 @@ ImageLongTitleAndDescription.args = { contentType: IMAGE_JPEG, }), }; -ImageLongTitleAndDescription.story = { - name: 'Image, Long Title And Description', -}; export const EverythingImageTitleDescriptionAndDate = Template.bind({}); EverythingImageTitleDescriptionAndDate.args = { @@ -125,6 +113,3 @@ EverythingImageTitleDescriptionAndDate.args = { contentType: IMAGE_JPEG, }), }; -EverythingImageTitleDescriptionAndDate.story = { - name: 'Everything: image, title, description, and date', -}; diff --git a/ts/components/conversation/StagedPlaceholderAttachment.stories.tsx b/ts/components/conversation/StagedPlaceholderAttachment.stories.tsx index d0cad5214948..57456b6b7ed0 100644 --- a/ts/components/conversation/StagedPlaceholderAttachment.stories.tsx +++ b/ts/components/conversation/StagedPlaceholderAttachment.stories.tsx @@ -3,16 +3,17 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import type { Props } from './StagedPlaceholderAttachment'; import { StagedPlaceholderAttachment } from './StagedPlaceholderAttachment'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/StagedPlaceholderAttachment', -}; +} satisfies Meta; export function Default(): JSX.Element { return ( diff --git a/ts/components/conversation/StagedPlaceholderAttachment.tsx b/ts/components/conversation/StagedPlaceholderAttachment.tsx index ede2c9a35fb8..39293e4292ad 100644 --- a/ts/components/conversation/StagedPlaceholderAttachment.tsx +++ b/ts/components/conversation/StagedPlaceholderAttachment.tsx @@ -4,7 +4,7 @@ import React from 'react'; import type { LocalizerType } from '../../types/Util'; -type Props = { +export type Props = { onClick: () => void; i18n: LocalizerType; }; diff --git a/ts/components/conversation/SystemMessage.stories.tsx b/ts/components/conversation/SystemMessage.stories.tsx index 30f87abee0da..e04e2bc16424 100644 --- a/ts/components/conversation/SystemMessage.stories.tsx +++ b/ts/components/conversation/SystemMessage.stories.tsx @@ -2,11 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; +import type { Meta } from '@storybook/react'; +import type { PropsType } from './SystemMessage'; import { SystemMessage, SystemMessageKind } from './SystemMessage'; export default { title: 'Components/Conversation/SystemMessage', -}; +} satisfies Meta; export function PlainSystemMessage(): JSX.Element { return ( @@ -18,10 +20,6 @@ export function PlainSystemMessage(): JSX.Element { ); } -PlainSystemMessage.story = { - name: 'Plain', -}; - export function DangerSystemMessage(): JSX.Element { return ( ); } - -ErrorSystemMessage.story = { - name: 'Error', -}; diff --git a/ts/components/conversation/SystemMessage.tsx b/ts/components/conversation/SystemMessage.tsx index df90b99c199a..2c977166e73a 100644 --- a/ts/components/conversation/SystemMessage.tsx +++ b/ts/components/conversation/SystemMessage.tsx @@ -11,7 +11,7 @@ export enum SystemMessageKind { Error = 'Error', } -type PropsType = { +export type PropsType = { icon: string; contents: ReactNode; button?: ReactNode; diff --git a/ts/components/conversation/Timeline.stories.tsx b/ts/components/conversation/Timeline.stories.tsx index 2567a189dda6..b8c1e2d8a92a 100644 --- a/ts/components/conversation/Timeline.stories.tsx +++ b/ts/components/conversation/Timeline.stories.tsx @@ -4,9 +4,8 @@ import * as React from 'react'; import { times } from 'lodash'; import { v4 as uuid } from 'uuid'; -import { text, boolean, number } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import { DurationInSeconds } from '../../util/durations'; import enMessages from '../../../_locales/en/messages.json'; @@ -32,7 +31,9 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/Timeline', -}; + argTypes: {}, + args: {}, +} satisfies Meta; // eslint-disable-next-line const noop = () => {}; @@ -405,12 +406,11 @@ const renderContactSpoofingReviewDialog = ( return ; }; -const getAbout = () => text('about', '👍 Free to chat'); -const getTitle = () => text('name', 'Cayce Bollard'); -const getProfileName = () => text('profileName', 'Cayce Bollard (profile)'); -const getAvatarPath = () => - text('avatarPath', '/fixtures/kitten-4-112-112.jpg'); -const getPhoneNumber = () => text('phoneNumber', '+1 (808) 555-1234'); +const getAbout = () => '👍 Free to chat'; +const getTitle = () => 'Cayce Bollard'; +const getProfileName = () => 'Cayce Bollard (profile)'; +const getAvatarPath = () => '/fixtures/kitten-4-112-112.jpg'; +const getPhoneNumber = () => '+1 (808) 555-1234'; const renderHeroRow = () => { function Wrapper() { @@ -463,22 +463,17 @@ const useProps = (overrideProps: Partial = {}): PropsType => ({ theme: React.useContext(StorybookThemeContext), getTimestampForMessage: Date.now, - haveNewest: boolean('haveNewest', overrideProps.haveNewest !== false), - haveOldest: boolean('haveOldest', overrideProps.haveOldest !== false), + haveNewest: overrideProps.haveNewest ?? false, + haveOldest: overrideProps.haveOldest ?? false, isConversationSelected: true, - isIncomingMessageRequest: boolean( - 'isIncomingMessageRequest', - overrideProps.isIncomingMessageRequest === true - ), - items: overrideProps.items || Object.keys(items), + isIncomingMessageRequest: overrideProps.isIncomingMessageRequest ?? false, + items: overrideProps.items ?? Object.keys(items), messageChangeCounter: 0, scrollToIndex: overrideProps.scrollToIndex, scrollToIndexCounter: 0, shouldShowMiniPlayer: Boolean(overrideProps.shouldShowMiniPlayer), - totalUnseen: number('totalUnseen', overrideProps.totalUnseen || 0), - oldestUnseenIndex: - number('oldestUnseenIndex', overrideProps.oldestUnseenIndex || 0) || - undefined, + totalUnseen: overrideProps.totalUnseen ?? 0, + oldestUnseenIndex: overrideProps.oldestUnseenIndex ?? 0, invitedContactsForNewlyCreatedGroup: overrideProps.invitedContactsForNewlyCreatedGroup || [], warning: overrideProps.warning, @@ -500,10 +495,6 @@ export function OldestAndNewest(): JSX.Element { return ; } -OldestAndNewest.story = { - name: 'Oldest and Newest', -}; - export function WithActiveMessageRequest(): JSX.Element { const props = useProps({ isIncomingMessageRequest: true, @@ -512,10 +503,6 @@ export function WithActiveMessageRequest(): JSX.Element { return ; } -WithActiveMessageRequest.story = { - name: 'With active message request', -}; - export function WithoutNewestMessage(): JSX.Element { const props = useProps({ haveNewest: false, @@ -533,10 +520,6 @@ export function WithoutNewestMessageActiveMessageRequest(): JSX.Element { return ; } -WithoutNewestMessageActiveMessageRequest.story = { - name: 'Without newest message, active message request', -}; - export function WithoutOldestMessage(): JSX.Element { const props = useProps({ haveOldest: false, @@ -554,10 +537,6 @@ export function EmptyJustHero(): JSX.Element { return ; } -EmptyJustHero.story = { - name: 'Empty (just hero)', -}; - export function LastSeen(): JSX.Element { const props = useProps({ oldestUnseenIndex: 13, @@ -575,10 +554,6 @@ export function TargetIndexToTop(): JSX.Element { return ; } -TargetIndexToTop.story = { - name: 'Target Index to Top', -}; - export function TypingIndicator(): JSX.Element { const props = useProps({ isSomeoneTyping: true }); @@ -602,10 +577,6 @@ export function WithInvitedContactsForANewlyCreatedGroup(): JSX.Element { return ; } -WithInvitedContactsForANewlyCreatedGroup.story = { - name: 'With invited contacts for a newly-created group', -}; - export function WithSameNameInDirectConversationWarning(): JSX.Element { const props = useProps({ warning: { @@ -618,10 +589,6 @@ export function WithSameNameInDirectConversationWarning(): JSX.Element { return ; } -WithSameNameInDirectConversationWarning.story = { - name: 'With "same name in direct conversation" warning', -}; - export function WithSameNameInGroupConversationWarning(): JSX.Element { const props = useProps({ warning: { @@ -638,10 +605,6 @@ export function WithSameNameInGroupConversationWarning(): JSX.Element { return ; } -WithSameNameInGroupConversationWarning.story = { - name: 'With "same name in group conversation" warning', -}; - export function WithJustMiniPlayer(): JSX.Element { const props = useProps({ shouldShowMiniPlayer: true, diff --git a/ts/components/conversation/TimelineFloatingHeader.stories.tsx b/ts/components/conversation/TimelineFloatingHeader.stories.tsx index b3b298306046..c67ca841a4cc 100644 --- a/ts/components/conversation/TimelineFloatingHeader.stories.tsx +++ b/ts/components/conversation/TimelineFloatingHeader.stories.tsx @@ -2,41 +2,36 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { isBoolean } from 'lodash'; -import { boolean } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; - import type { PropsType } from './TimelineFloatingHeader'; import { TimelineFloatingHeader } from './TimelineFloatingHeader'; const i18n = setupI18n('en', enMessages); -function booleanOr(value: boolean | undefined, defaultValue: boolean): boolean { - return isBoolean(value) ? value : defaultValue; -} - -const createProps = (overrideProps: Partial = {}): PropsType => ({ - isLoading: boolean('isLoading', booleanOr(overrideProps.isLoading, false)), - style: overrideProps.style, - visible: boolean('visible', booleanOr(overrideProps.visible, false)), - i18n, - timestamp: overrideProps.timestamp || Date.now(), -}); - export default { title: 'Components/TimelineFloatingHeader', -}; + argTypes: { + isLoading: { control: { type: 'boolean' } }, + visible: { control: { type: 'boolean' } }, + }, + args: { + isLoading: false, + visible: false, + i18n, + timestamp: Date.now(), + }, +} satisfies Meta; -export function Visible(): JSX.Element { - return ; +export function Default(args: PropsType): JSX.Element { + return ; } -export function Loading(): JSX.Element { - return ( - - ); +export function Visible(args: PropsType): JSX.Element { + return ; +} + +export function Loading(args: PropsType): JSX.Element { + return ; } diff --git a/ts/components/conversation/TimelineItem.stories.tsx b/ts/components/conversation/TimelineItem.stories.tsx index 97fb833ffcb6..9df960c28add 100644 --- a/ts/components/conversation/TimelineItem.stories.tsx +++ b/ts/components/conversation/TimelineItem.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { EmojiPicker } from '../emoji/EmojiPicker'; import { setupI18n } from '../../util/setupI18n'; import { DurationInSeconds } from '../../util/durations'; @@ -18,6 +17,7 @@ import { getDefaultConversation } from '../../test-both/helpers/getDefaultConver import { WidthBreakpoint } from '../_util'; import { ThemeType } from '../../types/Util'; import { PaymentEventKind } from '../../types/Payment'; +import { ErrorBoundary } from './ErrorBoundary'; const i18n = setupI18n('en', enMessages); @@ -122,7 +122,7 @@ const getDefaultProps = () => ({ export default { title: 'Components/Conversation/TimelineItem', -}; +} satisfies Meta; export function PlainMessage(): JSX.Element { const item = { @@ -584,7 +584,11 @@ export function UnknownType(): JSX.Element { // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any as TimelineItemProps['item']; - return ; + return ( + + + + ); } export function MissingItem(): JSX.Element { diff --git a/ts/components/conversation/TimelineMessage.stories.tsx b/ts/components/conversation/TimelineMessage.stories.tsx index 21d09a3e91a9..70072d60efb0 100644 --- a/ts/components/conversation/TimelineMessage.stories.tsx +++ b/ts/components/conversation/TimelineMessage.stories.tsx @@ -5,8 +5,7 @@ import * as React from 'react'; import { isBoolean, noop } from 'lodash'; import { action } from '@storybook/addon-actions'; -import { boolean, number, select, text } from '@storybook/addon-knobs'; -import type { Meta, Story } from '@storybook/react'; +import type { Meta, StoryFn } from '@storybook/react'; import { SignalService } from '../../protobuf'; import { ConversationColors } from '../../types/Colors'; @@ -68,19 +67,21 @@ export default { argTypes: { conversationType: { control: 'select', - defaultValue: 'direct', options: ['direct', 'group'], }, quote: { control: 'select', - defaultValue: undefined, mapping: quoteOptions, options: Object.keys(quoteOptions), }, }, -} as Meta; + args: { + conversationType: 'direct', + quote: undefined, + }, +} satisfies Meta; -const Template: Story> = args => { +const Template: StoryFn> = args => { return renderBothDirections({ ...createProps(), conversationType: 'direct', @@ -257,13 +258,9 @@ const createProps = (overrideProps: Partial = {}): Props => ({ clearTargetedMessage: action('clearSelectedMessage'), containerElementRef: React.createRef(), containerWidthBreakpoint: WidthBreakpoint.Wide, - conversationColor: - overrideProps.conversationColor || - select('conversationColor', ConversationColors, ConversationColors[0]), - conversationTitle: - overrideProps.conversationTitle || - text('conversationTitle', 'Conversation Title'), - conversationId: text('conversationId', overrideProps.conversationId || ''), + conversationColor: overrideProps.conversationColor ?? ConversationColors[0], + conversationTitle: overrideProps.conversationTitle ?? 'Conversation Title', + conversationId: overrideProps.conversationId ?? '', conversationType: overrideProps.conversationType || 'direct', contact: overrideProps.contact, deletedForEveryone: overrideProps.deletedForEveryone, @@ -272,17 +269,13 @@ const createProps = (overrideProps: Partial = {}): Props => ({ direction: overrideProps.direction || 'incoming', showLightboxForViewOnceMedia: action('showLightboxForViewOnceMedia'), doubleCheckMissingQuoteReference: action('doubleCheckMissingQuoteReference'), - expirationLength: - number('expirationLength', overrideProps.expirationLength || 0) || - undefined, - expirationTimestamp: - number('expirationTimestamp', overrideProps.expirationTimestamp || 0) || - undefined, + expirationLength: overrideProps.expirationLength ?? 0, + expirationTimestamp: overrideProps.expirationTimestamp ?? 0, getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined), giftBadge: overrideProps.giftBadge, i18n, platform: 'darwin', - id: text('id', overrideProps.id || 'random-message-id'), + id: overrideProps.id ?? 'random-message-id', // renderingContext: 'storybook', interactionMode: overrideProps.interactionMode || 'keyboard', isSticker: isBoolean(overrideProps.isSticker) @@ -355,15 +348,15 @@ const createProps = (overrideProps: Partial = {}): Props => ({ showLightbox: action('showLightbox'), startConversation: action('startConversation'), status: overrideProps.status || 'sent', - text: overrideProps.text || text('text', ''), + text: overrideProps.text ?? '', textDirection: overrideProps.textDirection || TextDirection.Default, textAttachment: overrideProps.textAttachment || { contentType: LONG_MESSAGE, size: 123, - pending: boolean('textPending', false), + pending: false, }, theme: ThemeType.light, - timestamp: number('timestamp', overrideProps.timestamp || Date.now()), + timestamp: overrideProps.timestamp ?? Date.now(), viewStory: action('viewStory'), }); @@ -403,9 +396,6 @@ PlainRtlMessage.args = { text: 'الأسانسير، علشان القطط ماتاكلش منها. وننساها، ونعود الى أوراقنا موصدين الباب بإحكام. نتنحنح، ونقول: البتاع. كلمة تدلّ على لا شيء، وعلى كلّ شيء. وهي مركز أبحاث شعبية كثيرة، تتعجّب من غرابتها والقومية المصرية الخاصة التي تعكسها، الى جانب الشيء الكثير من العفوية وحلاوة الروح. نعم، نحن قرأنا وسمعنا وعرفنا كل هذا. لكنه محلّ اهتمامنا اليوم لأسباب غير تلك الأسباب. كذلك، فإننا لعاقدون عزمنا على أن نتجاوز قضية الفصحى والعامية، وثنائية النخبة والرعاع، التي كثيراً ما ينحو نحوها الحديث عن الكلمة المذكورة. وفوق هذا كله، لسنا بصدد تفسير معاني "البتاع" كما تأتي في قصيدة الحاج أحمد فؤاد نجم، ولا التحذلق والتفذلك في الألغاز والأسرار المكنونة. هذا البتاع - أم هذه البت', textDirection: TextDirection.RightToLeft, }; -PlainRtlMessage.story = { - name: 'Plain RTL Message', -}; export function EmojiMessages(): JSX.Element { return ( @@ -538,9 +528,6 @@ WillExpireButStillSending.args = { expirationLength: 30 * 1000, text: 'We always show the timer if a message has an expiration length, even if unread or still sending.', }; -WillExpireButStillSending.story = { - name: 'Will expire but still sending', -}; export const Pending = Template.bind({}); Pending.args = { @@ -564,9 +551,6 @@ LongBodyCanBeDownloaded.args = { key: 'def', }, }; -LongBodyCanBeDownloaded.story = { - name: 'Long body can be downloaded', -}; export const Recent = Template.bind({}); Recent.args = { @@ -658,9 +642,6 @@ ReactionsWiderMessage.args = { }, ], }; -ReactionsWiderMessage.story = { - name: 'Reactions (wider message)', -}; const joyReactions = Array.from({ length: 52 }, () => getJoyReaction()); @@ -734,10 +715,6 @@ ReactionsShortMessage.args = { ], }; -ReactionsShortMessage.story = { - name: 'Reactions (short message)', -}; - export const AvatarInGroup = Template.bind({}); AvatarInGroup.args = { author: getDefaultConversation({ avatarPath: pngUrl }), @@ -745,9 +722,6 @@ AvatarInGroup.args = { status: 'sent', text: 'Hello it is me, the saxophone.', }; -AvatarInGroup.story = { - name: 'Avatar in Group', -}; export const BadgeInGroup = Template.bind({}); BadgeInGroup.args = { @@ -756,9 +730,6 @@ BadgeInGroup.args = { status: 'sent', text: 'Hello it is me, the saxophone.', }; -BadgeInGroup.story = { - name: 'Badge in Group', -}; export const Sticker = Template.bind({}); Sticker.args = { @@ -829,9 +800,6 @@ DeletedWithExpireTimer.args = { expirationTimestamp: Date.now() + 3 * 60 * 1000, status: 'sent', }; -DeletedWithExpireTimer.story = { - name: 'Deleted with expireTimer', -}; export function DeletedWithError(): JSX.Element { const propsPartialError = createProps({ @@ -858,9 +826,6 @@ export function DeletedWithError(): JSX.Element { ); } -DeletedWithError.story = { - name: 'Deleted with error', -}; export const CanDeleteForEveryone = Template.bind({}); CanDeleteForEveryone.args = { @@ -869,9 +834,6 @@ CanDeleteForEveryone.args = { // canDeleteForEveryone: true, direction: 'outgoing', }; -CanDeleteForEveryone.story = { - name: 'Can delete for everyone', -}; export const Error = Template.bind({}); Error.args = { @@ -923,9 +885,6 @@ LinkPreviewInGroup.args = { text: 'Be sure to look at https://www.signal.org', conversationType: 'group', }; -LinkPreviewInGroup.story = { - name: 'Link Preview in Group', -}; export const LinkPreviewWithQuote = Template.bind({}); LinkPreviewWithQuote.args = { @@ -963,9 +922,6 @@ LinkPreviewWithQuote.args = { text: 'Be sure to look at https://www.signal.org', conversationType: 'group', }; -LinkPreviewWithQuote.story = { - name: 'Link Preview with Quote', -}; export const LinkPreviewWithSmallImage = Template.bind({}); LinkPreviewWithSmallImage.args = { @@ -990,9 +946,6 @@ LinkPreviewWithSmallImage.args = { status: 'sent', text: 'Be sure to look at https://www.signal.org', }; -LinkPreviewWithSmallImage.story = { - name: 'Link Preview with Small Image', -}; export const LinkPreviewWithoutImage = Template.bind({}); LinkPreviewWithoutImage.args = { @@ -1010,9 +963,6 @@ LinkPreviewWithoutImage.args = { status: 'sent', text: 'Be sure to look at https://www.signal.org', }; -LinkPreviewWithoutImage.story = { - name: 'Link Preview without Image', -}; export const LinkPreviewWithNoDescription = Template.bind({}); LinkPreviewWithNoDescription.args = { @@ -1028,9 +978,6 @@ LinkPreviewWithNoDescription.args = { status: 'sent', text: 'Be sure to look at https://www.signal.org', }; -LinkPreviewWithNoDescription.story = { - name: 'Link Preview with no description', -}; export const LinkPreviewWithLongDescription = Template.bind({}); LinkPreviewWithLongDescription.args = { @@ -1051,9 +998,6 @@ LinkPreviewWithLongDescription.args = { status: 'sent', text: 'Be sure to look at https://www.signal.org', }; -LinkPreviewWithLongDescription.story = { - name: 'Link Preview with long description', -}; export const LinkPreviewWithSmallImageLongDescription = Template.bind({}); LinkPreviewWithSmallImageLongDescription.args = { @@ -1081,9 +1025,6 @@ LinkPreviewWithSmallImageLongDescription.args = { status: 'sent', text: 'Be sure to look at https://www.signal.org', }; -LinkPreviewWithSmallImageLongDescription.story = { - name: 'Link Preview with small image, long description', -}; export const LinkPreviewWithNoDate = Template.bind({}); LinkPreviewWithNoDate.args = { @@ -1107,9 +1048,6 @@ LinkPreviewWithNoDate.args = { status: 'sent', text: 'Be sure to look at https://www.signal.org', }; -LinkPreviewWithNoDate.story = { - name: 'Link Preview with no date', -}; export const LinkPreviewWithTooNewADate = Template.bind({}); LinkPreviewWithTooNewADate.args = { @@ -1134,9 +1072,6 @@ LinkPreviewWithTooNewADate.args = { status: 'sent', text: 'Be sure to look at https://www.signal.org', }; -LinkPreviewWithTooNewADate.story = { - name: 'Link Preview with too new a date', -}; export function Image(): JSX.Element { const darkImageProps = createProps({ @@ -1312,9 +1247,6 @@ ImageWithCaption.args = { status: 'sent', text: 'This is my home.', }; -ImageWithCaption.story = { - name: 'Image with Caption', -}; export const Gif = Template.bind({}); Gif.args = { @@ -1330,9 +1262,6 @@ Gif.args = { ], status: 'sent', }; -Gif.story = { - name: 'GIF', -}; export const GifInAGroup = Template.bind({}); GifInAGroup.args = { @@ -1349,9 +1278,6 @@ GifInAGroup.args = { conversationType: 'group', status: 'sent', }; -GifInAGroup.story = { - name: 'GIF in a group', -}; export const NotDownloadedGif = Template.bind({}); NotDownloadedGif.args = { @@ -1368,9 +1294,6 @@ NotDownloadedGif.args = { ], status: 'sent', }; -NotDownloadedGif.story = { - name: 'Not Downloaded GIF', -}; export const PendingGif = Template.bind({}); PendingGif.args = { @@ -1388,9 +1311,6 @@ PendingGif.args = { ], status: 'sent', }; -PendingGif.story = { - name: 'Pending GIF', -}; export const _Audio = (): JSX.Element => { function Wrapper() { @@ -1465,9 +1385,6 @@ AudioWithCaption.args = { status: 'sent', text: 'This is what I sound like.', }; -AudioWithCaption.story = { - name: 'Audio with Caption', -}; export const AudioWithNotDownloadedAttachment = Template.bind({}); AudioWithNotDownloadedAttachment.args = { @@ -1479,9 +1396,6 @@ AudioWithNotDownloadedAttachment.args = { ], status: 'sent', }; -AudioWithNotDownloadedAttachment.story = { - name: 'Audio with Not Downloaded Attachment', -}; export const AudioWithPendingAttachment = Template.bind({}); AudioWithPendingAttachment.args = { @@ -1494,9 +1408,6 @@ AudioWithPendingAttachment.args = { ], status: 'sent', }; -AudioWithPendingAttachment.story = { - name: 'Audio with Pending Attachment', -}; export const OtherFileType = Template.bind({}); OtherFileType.args = { @@ -1524,9 +1435,6 @@ OtherFileTypeWithCaption.args = { status: 'sent', text: 'This is what I have done.', }; -OtherFileTypeWithCaption.story = { - name: 'Other File Type with Caption', -}; export const OtherFileTypeWithLongFilename = Template.bind({}); OtherFileTypeWithLongFilename.args = { @@ -1542,9 +1450,6 @@ OtherFileTypeWithLongFilename.args = { status: 'sent', text: 'This is what I have done.', }; -OtherFileTypeWithLongFilename.story = { - name: 'Other File Type with Long Filename', -}; export const TapToViewImage = Template.bind({}); TapToViewImage.args = { @@ -1560,9 +1465,6 @@ TapToViewImage.args = { isTapToView: true, status: 'sent', }; -TapToViewImage.story = { - name: 'TapToView Image', -}; export const TapToViewVideo = Template.bind({}); TapToViewVideo.args = { @@ -1578,9 +1480,6 @@ TapToViewVideo.args = { isTapToView: true, status: 'sent', }; -TapToViewVideo.story = { - name: 'TapToView Video', -}; export const TapToViewGif = Template.bind({}); TapToViewGif.args = { @@ -1597,9 +1496,6 @@ TapToViewGif.args = { isTapToView: true, status: 'sent', }; -TapToViewGif.story = { - name: 'TapToView GIF', -}; export const TapToViewExpired = Template.bind({}); TapToViewExpired.args = { @@ -1616,9 +1512,6 @@ TapToViewExpired.args = { isTapToViewExpired: true, status: 'sent', }; -TapToViewExpired.story = { - name: 'TapToView Expired', -}; export const TapToViewError = Template.bind({}); TapToViewError.args = { @@ -1635,9 +1528,6 @@ TapToViewError.args = { isTapToViewError: true, status: 'sent', }; -TapToViewError.story = { - name: 'TapToView Error', -}; export const DangerousFileType = Template.bind({}); DangerousFileType.args = { @@ -1683,9 +1573,6 @@ Mentions.args = { ], text: '\uFFFC This Is It. The Moment We Should Have Trained For.', }; -Mentions.story = { - name: '@Mentions', -}; export function AllTheContextMenus(): JSX.Element { const props = createProps({ @@ -1706,9 +1593,6 @@ export function AllTheContextMenus(): JSX.Element { return ; } -AllTheContextMenus.story = { - name: 'All the context menus', -}; export const NotApprovedWithLinkPreview = Template.bind({}); NotApprovedWithLinkPreview.args = { @@ -1734,9 +1618,6 @@ NotApprovedWithLinkPreview.args = { text: 'Be sure to look at https://www.signal.org', isMessageRequestAccepted: false, }; -NotApprovedWithLinkPreview.story = { - name: 'Not approved, with link preview', -}; export function CustomColor(): JSX.Element { return ( @@ -1802,10 +1683,6 @@ export const CollapsingTextOnlyDMs = (): JSX.Element => { ]); }; -CollapsingTextOnlyDMs.story = { - name: 'Collapsing text-only DMs', -}; - export const CollapsingTextOnlyGroupMessages = (): JSX.Element => { const author = getDefaultConversation(); @@ -1830,10 +1707,6 @@ export const CollapsingTextOnlyGroupMessages = (): JSX.Element => { ]); }; -CollapsingTextOnlyGroupMessages.story = { - name: 'Collapsing text-only group messages', -}; - export const StoryReply = (): JSX.Element => { const conversation = getDefaultConversation(); @@ -1852,10 +1725,6 @@ export const StoryReply = (): JSX.Element => { }); }; -StoryReply.story = { - name: 'Story reply', -}; - export const StoryReplyYours = (): JSX.Element => { const conversation = getDefaultConversation(); @@ -1874,10 +1743,6 @@ export const StoryReplyYours = (): JSX.Element => { }); }; -StoryReplyYours.story = { - name: 'Story reply (yours)', -}; - export const StoryReplyEmoji = (): JSX.Element => { const conversation = getDefaultConversation(); @@ -1897,10 +1762,6 @@ export const StoryReplyEmoji = (): JSX.Element => { }); }; -StoryReplyEmoji.story = { - name: 'Story reply (emoji)', -}; - const fullContact = { avatar: { avatar: fakeAttachment({ @@ -1935,9 +1796,6 @@ export const EmbeddedContactFullContact = Template.bind({}); EmbeddedContactFullContact.args = { contact: fullContact, }; -EmbeddedContactFullContact.story = { - name: 'EmbeddedContact: Full Contact', -}; export const EmbeddedContactWithSendMessage = Template.bind({}); EmbeddedContactWithSendMessage.args = { @@ -1948,9 +1806,6 @@ EmbeddedContactWithSendMessage.args = { }, direction: 'incoming', }; -EmbeddedContactWithSendMessage.story = { - name: 'EmbeddedContact: with Send Message', -}; export const EmbeddedContactOnlyEmail = Template.bind({}); EmbeddedContactOnlyEmail.args = { @@ -1958,9 +1813,6 @@ EmbeddedContactOnlyEmail.args = { email: fullContact.email, }, }; -EmbeddedContactOnlyEmail.story = { - name: 'EmbeddedContact: Only Email', -}; export const EmbeddedContactGivenName = Template.bind({}); EmbeddedContactGivenName.args = { @@ -1970,9 +1822,6 @@ EmbeddedContactGivenName.args = { }, }, }; -EmbeddedContactGivenName.story = { - name: 'EmbeddedContact: Given Name', -}; export const EmbeddedContactOrganization = Template.bind({}); EmbeddedContactOrganization.args = { @@ -1980,9 +1829,6 @@ EmbeddedContactOrganization.args = { organization: 'Company 5', }, }; -EmbeddedContactOrganization.story = { - name: 'EmbeddedContact: Organization', -}; export const EmbeddedContactGivenFamilyName = Template.bind({}); EmbeddedContactGivenFamilyName.args = { @@ -1993,9 +1839,6 @@ EmbeddedContactGivenFamilyName.args = { }, }, }; -EmbeddedContactGivenFamilyName.story = { - name: 'EmbeddedContact: Given + Family Name', -}; export const EmbeddedContactFamilyName = Template.bind({}); EmbeddedContactFamilyName.args = { @@ -2005,9 +1848,6 @@ EmbeddedContactFamilyName.args = { }, }, }; -EmbeddedContactFamilyName.story = { - name: 'EmbeddedContact: Family Name', -}; export const EmbeddedContactLoadingAvatar = Template.bind({}); EmbeddedContactLoadingAvatar.args = { @@ -2024,9 +1864,6 @@ EmbeddedContactLoadingAvatar.args = { }, }, }; -EmbeddedContactLoadingAvatar.story = { - name: 'EmbeddedContact: Loading Avatar', -}; export const GiftBadgeUnopened = Template.bind({}); GiftBadgeUnopened.args = { @@ -2037,9 +1874,6 @@ GiftBadgeUnopened.args = { state: GiftBadgeStates.Unopened, }, }; -GiftBadgeUnopened.story = { - name: 'Gift Badge: Unopened', -}; const getPreferredBadge = () => ({ category: BadgeCategory.Donor, @@ -2066,9 +1900,6 @@ GiftBadgeRedeemed30Days.args = { state: GiftBadgeStates.Redeemed, }, }; -GiftBadgeRedeemed30Days.story = { - name: 'Gift Badge: Redeemed (30 days)', -}; export const GiftBadgeRedeemed24Hours = Template.bind({}); GiftBadgeRedeemed24Hours.args = { @@ -2080,9 +1911,6 @@ GiftBadgeRedeemed24Hours.args = { state: GiftBadgeStates.Redeemed, }, }; -GiftBadgeRedeemed24Hours.story = { - name: 'Gift Badge: Redeemed (24 hours)', -}; export const GiftBadgeOpened60Minutes = Template.bind({}); GiftBadgeOpened60Minutes.args = { @@ -2094,9 +1922,6 @@ GiftBadgeOpened60Minutes.args = { state: GiftBadgeStates.Opened, }, }; -GiftBadgeOpened60Minutes.story = { - name: 'Gift Badge: Opened (60 minutes)', -}; export const GiftBadgeRedeemed1Minute = Template.bind({}); GiftBadgeRedeemed1Minute.args = { @@ -2108,9 +1933,6 @@ GiftBadgeRedeemed1Minute.args = { state: GiftBadgeStates.Redeemed, }, }; -GiftBadgeRedeemed1Minute.story = { - name: 'Gift Badge: Redeemed (1 minute)', -}; export const GiftBadgeOpenedExpired = Template.bind({}); GiftBadgeOpenedExpired.args = { @@ -2122,9 +1944,6 @@ GiftBadgeOpenedExpired.args = { state: GiftBadgeStates.Opened, }, }; -GiftBadgeOpenedExpired.story = { - name: 'Gift Badge: Opened (expired)', -}; export const GiftBadgeMissingBadge = Template.bind({}); GiftBadgeMissingBadge.args = { @@ -2136,9 +1955,6 @@ GiftBadgeMissingBadge.args = { state: GiftBadgeStates.Redeemed, }, }; -GiftBadgeMissingBadge.story = { - name: 'Gift Badge: Missing Badge', -}; export const PaymentNotification = Template.bind({}); PaymentNotification.args = { diff --git a/ts/components/conversation/TimerNotification.stories.tsx b/ts/components/conversation/TimerNotification.stories.tsx index 25d2f2b07ecb..3b19c9c29859 100644 --- a/ts/components/conversation/TimerNotification.stories.tsx +++ b/ts/components/conversation/TimerNotification.stories.tsx @@ -2,8 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { boolean, number, select, text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import { DurationInSeconds } from '../../util/durations'; import enMessages from '../../../_locales/en/messages.json'; @@ -14,42 +13,31 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/TimerNotification', -}; - -const createProps = (overrideProps: Partial = {}): Props => ({ - i18n, - type: select( - 'type', - { - fromOther: 'fromOther', - fromMe: 'fromMe', - fromSync: 'fromSync', + argTypes: { + type: { + control: { type: 'select' }, + options: ['fromOther', 'fromMe', 'fromSync'], }, - overrideProps.type || 'fromOther' - ), - title: text('title', overrideProps.title || ''), - ...(boolean('disabled', overrideProps.disabled || false) - ? { - disabled: true, - } - : { - disabled: false, - expireTimer: DurationInSeconds.fromMillis( - number( - 'expireTimer', - ('expireTimer' in overrideProps ? overrideProps.expireTimer : 0) || - 0 - ) - ), - }), -}); + disabled: { control: { type: 'boolean' } }, + expireTimer: { control: { type: 'number' } }, + }, + args: { + i18n, + type: 'fromOther', + title: '', + disabled: false, + expireTimer: DurationInSeconds.fromHours(0), + }, +} satisfies Meta; -export function SetByOther(): JSX.Element { - const props = createProps({ +export function SetByOther(args: Props): JSX.Element { + const props: Props = { + ...args, + disabled: false, expireTimer: DurationInSeconds.fromHours(1), type: 'fromOther', title: 'Mr. Fire', - }); + }; return ( <> @@ -60,14 +48,16 @@ export function SetByOther(): JSX.Element { ); } -export function SetByOtherWithALongName(): JSX.Element { +export function SetByOtherWithALongName(args: Props): JSX.Element { const longName = '🦴🧩📴'.repeat(50); - const props = createProps({ + const props: Props = { + ...args, + disabled: false, expireTimer: DurationInSeconds.fromHours(1), type: 'fromOther', title: longName, - }); + }; return ( <> @@ -78,16 +68,14 @@ export function SetByOtherWithALongName(): JSX.Element { ); } -SetByOtherWithALongName.story = { - name: 'Set By Other (with a long name)', -}; - -export function SetByYou(): JSX.Element { - const props = createProps({ +export function SetByYou(args: Props): JSX.Element { + const props: Props = { + ...args, + disabled: false, expireTimer: DurationInSeconds.fromHours(1), type: 'fromMe', title: 'Mr. Fire', - }); + }; return ( <> @@ -98,12 +86,14 @@ export function SetByYou(): JSX.Element { ); } -export function SetBySync(): JSX.Element { - const props = createProps({ +export function SetBySync(args: Props): JSX.Element { + const props: Props = { + ...args, + disabled: false, expireTimer: DurationInSeconds.fromHours(1), type: 'fromSync', title: 'Mr. Fire', - }); + }; return ( <> diff --git a/ts/components/conversation/TypingAnimation.stories.tsx b/ts/components/conversation/TypingAnimation.stories.tsx index 5d24c54864d5..714f832fc5d7 100644 --- a/ts/components/conversation/TypingAnimation.stories.tsx +++ b/ts/components/conversation/TypingAnimation.stories.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { Props } from './TypingAnimation'; @@ -12,7 +12,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/TypingAnimation', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ i18n, diff --git a/ts/components/conversation/TypingBubble.stories.tsx b/ts/components/conversation/TypingBubble.stories.tsx index 6c892383ab46..2b6b093faee7 100644 --- a/ts/components/conversation/TypingBubble.stories.tsx +++ b/ts/components/conversation/TypingBubble.stories.tsx @@ -4,8 +4,7 @@ import React, { useEffect, useState } from 'react'; import { times } from 'lodash'; import { action } from '@storybook/addon-actions'; -import { date, select } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; @@ -19,7 +18,9 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/TypingBubble', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const CONTACTS = times(10, index => { const letter = (index + 10).toString(36).toUpperCase(); @@ -52,9 +53,7 @@ const getConversationWithBadges = (id: string) => CONTACTS_WITH_BADGES_BY_ID.get(id) || getDefaultConversation(); const getTypingContactIdTimestamps = (count: number) => - Object.fromEntries( - CONTACT_IDS.slice(0, count).map(id => [id, date('timestamp', new Date())]) - ); + Object.fromEntries(CONTACT_IDS.slice(0, count).map(id => [id, Date.now()])); const createProps = ( overrideProps: Partial = {} @@ -67,13 +66,7 @@ const createProps = ( lastItemTimestamp: undefined, i18n, conversationId: '123', - conversationType: - overrideProps.conversationType || - select( - 'conversationType', - { group: 'group', direct: 'direct' }, - 'direct' - ), + conversationType: overrideProps.conversationType ?? 'direct', getConversation: overrideProps.getConversation || getConversation, getPreferredBadge: badges => badges.length > 0 ? getFakeBadge() : undefined, @@ -109,10 +102,6 @@ export function Group(): JSX.Element { return ; } -Group.story = { - name: 'Group (1 person typing)', -}; - export function GroupStartsTyping(): JSX.Element { const props = createProps({ conversationType: 'group', @@ -132,10 +121,6 @@ export function GroupStartsTyping(): JSX.Element { return ; } -GroupStartsTyping.story = { - name: 'Group (0 to 1 person starts typing)', -}; - export function GroupStoppedTyping(): JSX.Element { const props = createProps({ conversationType: 'group', @@ -152,10 +137,6 @@ export function GroupStoppedTyping(): JSX.Element { return ; } -GroupStoppedTyping.story = { - name: 'Group (1 person stopped typing)', -}; - export function GroupWithBadge(): JSX.Element { const props = createProps({ conversationType: 'group', @@ -166,10 +147,6 @@ export function GroupWithBadge(): JSX.Element { return ; } -GroupWithBadge.story = { - name: 'Group (with badge)', -}; - export function GroupMultiTyping1To2(): JSX.Element { const props = createProps({ conversationType: 'group', @@ -189,10 +166,6 @@ export function GroupMultiTyping1To2(): JSX.Element { return ; } -GroupMultiTyping1To2.story = { - name: 'Group (1 to 2 persons)', -}; - export function GroupMultiTyping2Then1PersonStops(): JSX.Element { const props = createProps({ conversationType: 'group', @@ -212,10 +185,6 @@ export function GroupMultiTyping2Then1PersonStops(): JSX.Element { return ; } -GroupMultiTyping2Then1PersonStops.story = { - name: 'Group (2 persons typing then 1 person stops)', -}; - export function GroupMultiTyping3To4(): JSX.Element { const props = createProps({ conversationType: 'group', @@ -235,10 +204,6 @@ export function GroupMultiTyping3To4(): JSX.Element { return ; } -GroupMultiTyping3To4.story = { - name: 'Group (3 to 4)', -}; - export function GroupMultiTyping10(): JSX.Element { const props = createProps({ conversationType: 'group', @@ -248,10 +213,6 @@ export function GroupMultiTyping10(): JSX.Element { return ; } -GroupMultiTyping10.story = { - name: 'Group (10 persons typing)', -}; - export function GroupMultiTypingWithBadges(): JSX.Element { const props = createProps({ conversationType: 'group', @@ -261,7 +222,3 @@ export function GroupMultiTypingWithBadges(): JSX.Element { return ; } - -GroupMultiTypingWithBadges.story = { - name: 'Group (3 persons typing, 2 persons have badge)', -}; diff --git a/ts/components/conversation/UniversalTimerNotification.stories.tsx b/ts/components/conversation/UniversalTimerNotification.stories.tsx index e6464a9ad50c..6c89ffa3e776 100644 --- a/ts/components/conversation/UniversalTimerNotification.stories.tsx +++ b/ts/components/conversation/UniversalTimerNotification.stories.tsx @@ -2,16 +2,16 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - +import type { Meta } from '@storybook/react'; +import type { Props } from './UniversalTimerNotification'; import { UniversalTimerNotification } from './UniversalTimerNotification'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; - import { EXPIRE_TIMERS } from '../../test-both/util/expireTimers'; export default { title: 'Components/UniversalTimerNotification', -}; +} satisfies Meta; const i18n = setupI18n('en', enMessages); diff --git a/ts/components/conversation/UnsupportedMessage.stories.tsx b/ts/components/conversation/UnsupportedMessage.stories.tsx index 5b6f70974a6c..5af325a46b8d 100644 --- a/ts/components/conversation/UnsupportedMessage.stories.tsx +++ b/ts/components/conversation/UnsupportedMessage.stories.tsx @@ -2,8 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { boolean, text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { ContactType, Props } from './UnsupportedMessage'; @@ -13,62 +12,56 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/UnsupportedMessage', -}; + argTypes: { + canProcessNow: { control: { type: 'boolean' } }, + }, + args: { + i18n, + canProcessNow: false, + contact: {} as ContactType, + }, +} satisfies Meta; const createContact = (props: Partial = {}): ContactType => ({ id: '', - title: text('contact title', props.title || ''), - isMe: boolean('contact isMe', props.isMe || false), + title: props.title ?? '', + isMe: props.isMe ?? false, }); -const createProps = (overrideProps: Partial = {}): Props => ({ - i18n, - canProcessNow: boolean('canProcessNow', overrideProps.canProcessNow || false), - contact: overrideProps.contact || ({} as ContactType), -}); - -export function FromSomeone(): JSX.Element { +export function FromSomeone(args: Props): JSX.Element { const contact = createContact({ title: 'Alice', name: 'Alice', }); - const props = createProps({ contact }); - - return ; + return ; } -export function AfterUpgrade(): JSX.Element { +export function AfterUpgrade(args: Props): JSX.Element { const contact = createContact({ title: 'Alice', name: 'Alice', }); - const props = createProps({ contact, canProcessNow: true }); - - return ; + return ; } -export function FromYourself(): JSX.Element { +export function FromYourself(args: Props): JSX.Element { const contact = createContact({ title: 'Alice', name: 'Alice', isMe: true, }); - const props = createProps({ contact }); - - return ; + return ; } -export function FromYourselfAfterUpgrade(): JSX.Element { +export function FromYourselfAfterUpgrade(args: Props): JSX.Element { const contact = createContact({ title: 'Alice', name: 'Alice', isMe: true, }); - const props = createProps({ contact, canProcessNow: true }); - - return ; + return ; } diff --git a/ts/components/conversation/VerificationNotification.stories.tsx b/ts/components/conversation/VerificationNotification.stories.tsx index cafe88c42f5f..473883c5e5be 100644 --- a/ts/components/conversation/VerificationNotification.stories.tsx +++ b/ts/components/conversation/VerificationNotification.stories.tsx @@ -2,8 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { boolean } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { Props } from './VerificationNotification'; @@ -11,70 +10,58 @@ import { VerificationNotification } from './VerificationNotification'; const i18n = setupI18n('en', enMessages); -export default { - title: 'Components/Conversation/VerificationNotification', -}; - const contact = { title: 'Mr. Fire' }; -const createProps = (overrideProps: Partial = {}): Props => ({ - i18n, - type: overrideProps.type || 'markVerified', - isLocal: boolean('isLocal', overrideProps.isLocal !== false), - contact: overrideProps.contact || contact, -}); - -export function MarkAsVerified(): JSX.Element { - const props = createProps({ type: 'markVerified' }); - - return ; -} - -MarkAsVerified.story = { - name: 'Mark as Verified', -}; - -export function MarkAsNotVerified(): JSX.Element { - const props = createProps({ type: 'markNotVerified' }); - - return ; -} - -MarkAsNotVerified.story = { - name: 'Mark as Not Verified', -}; - -export function MarkAsVerifiedRemotely(): JSX.Element { - const props = createProps({ type: 'markVerified', isLocal: false }); - - return ; -} - -MarkAsVerifiedRemotely.story = { - name: 'Mark as Verified Remotely', -}; - -export function MarkAsNotVerifiedRemotely(): JSX.Element { - const props = createProps({ type: 'markNotVerified', isLocal: false }); - - return ; -} - -MarkAsNotVerifiedRemotely.story = { - name: 'Mark as Not Verified Remotely', -}; - -export function LongName(): JSX.Element { - const longName = '🎆🍬🏈'.repeat(50); - - const props = createProps({ +export default { + title: 'Components/Conversation/VerificationNotification', + argTypes: { + type: { + control: { + type: 'select', + options: ['markVerified', 'markNotVerified'], + }, + }, + isLocal: { control: { type: 'boolean' } }, + }, + args: { + i18n, type: 'markVerified', - contact: { title: longName }, - }); + isLocal: true, + contact, + }, +} satisfies Meta; - return ; +export function MarkAsVerified(args: Props): JSX.Element { + return ; } -LongName.story = { - name: 'Long name', -}; +export function MarkAsNotVerified(args: Props): JSX.Element { + return ; +} + +export function MarkAsVerifiedRemotely(args: Props): JSX.Element { + return ( + + ); +} + +export function MarkAsNotVerifiedRemotely(args: Props): JSX.Element { + return ( + + ); +} + +export function LongName(args: Props): JSX.Element { + const longName = '🎆🍬🏈'.repeat(50); + return ( + + ); +} diff --git a/ts/components/conversation/conversation-details/AddGroupMembersModal.stories.tsx b/ts/components/conversation/conversation-details/AddGroupMembersModal.stories.tsx index dd24b2463cd3..4ae78fb697dd 100644 --- a/ts/components/conversation/conversation-details/AddGroupMembersModal.stories.tsx +++ b/ts/components/conversation/conversation-details/AddGroupMembersModal.stories.tsx @@ -4,9 +4,8 @@ import type { ComponentProps } from 'react'; import React, { useState } from 'react'; import { times } from 'lodash'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { sleep } from '../../../util/sleep'; import { makeLookup } from '../../../util/makeLookup'; import { deconstructLookup } from '../../../util/deconstructLookup'; @@ -25,7 +24,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/AddGroupMembersModal', -}; +} satisfies Meta; const allCandidateContacts = times(50, () => getDefaultConversation()); let allCandidateContactsLookup = makeLookup(allCandidateContacts, 'id'); @@ -101,18 +100,10 @@ export function Only3Contacts(): JSX.Element { ); } -Only3Contacts.story = { - name: 'Only 3 contacts', -}; - export function NoCandidateContacts(): JSX.Element { return ; } -NoCandidateContacts.story = { - name: 'No candidate contacts', -}; - export function EveryoneAlreadyAdded(): JSX.Element { return ( ; } - -RequestFailsAfter1Second.story = { - name: 'Request fails after 1 second', -}; diff --git a/ts/components/conversation/conversation-details/ConversationDetails.stories.tsx b/ts/components/conversation/conversation-details/ConversationDetails.stories.tsx index 6b1ddc18c5d7..3bc57f7a3480 100644 --- a/ts/components/conversation/conversation-details/ConversationDetails.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetails.stories.tsx @@ -6,6 +6,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; import { times } from 'lodash'; +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import type { Props } from './ConversationDetails'; @@ -29,7 +30,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/ConversationDetails', -}; +} satisfies Meta; const conversation: ConversationType = getDefaultConversation({ id: '', @@ -145,10 +146,6 @@ export function AsAdmin(): JSX.Element { return ; } -AsAdmin.story = { - name: 'as Admin', -}; - export function AsLastAdmin(): JSX.Element { const props = createProps(); @@ -166,10 +163,6 @@ export function AsLastAdmin(): JSX.Element { ); } -AsLastAdmin.story = { - name: 'as last admin', -}; - export function AsOnlyAdmin(): JSX.Element { const props = createProps(); @@ -189,10 +182,6 @@ export function AsOnlyAdmin(): JSX.Element { ); } -AsOnlyAdmin.story = { - name: 'as only admin', -}; - export function GroupEditable(): JSX.Element { const props = createProps(); @@ -205,10 +194,6 @@ export function GroupEditableWithCustomDisappearingTimeout(): JSX.Element { return ; } -GroupEditableWithCustomDisappearingTimeout.story = { - name: 'Group Editable with custom disappearing timeout', -}; - export function GroupLinksOn(): JSX.Element { const props = createProps(true); @@ -219,10 +204,6 @@ export const _11 = (): JSX.Element => ( ); -_11.story = { - name: '1:1', -}; - function mins(n: number) { return DurationInSeconds.toMillis(DurationInSeconds.fromMinutes(n)); } diff --git a/ts/components/conversation/conversation-details/ConversationDetailsActions.stories.tsx b/ts/components/conversation/conversation-details/ConversationDetailsActions.stories.tsx index ed21a73798c3..f18681bac542 100644 --- a/ts/components/conversation/conversation-details/ConversationDetailsActions.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetailsActions.stories.tsx @@ -3,9 +3,8 @@ import * as React from 'react'; import { isBoolean } from 'lodash'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import type { Props } from './ConversationDetailsActions'; @@ -16,7 +15,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/ConversationDetailsActions', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ acceptConversation: action('acceptConversation'), @@ -47,10 +46,6 @@ export function LeftTheGroup(): JSX.Element { return ; } -LeftTheGroup.story = { - name: 'Left the group', -}; - export function BlockedAndLeftTheGroup(): JSX.Element { const props = createProps({ left: true, @@ -61,32 +56,16 @@ export function BlockedAndLeftTheGroup(): JSX.Element { return ; } -BlockedAndLeftTheGroup.story = { - name: 'Blocked and left the group', -}; - export function CannotLeaveBecauseYouAreTheLastAdmin(): JSX.Element { const props = createProps({ cannotLeaveBecauseYouAreLastAdmin: true }); return ; } -CannotLeaveBecauseYouAreTheLastAdmin.story = { - name: 'Cannot leave because you are the last admin', -}; - export const _11 = (): JSX.Element => ( ); -_11.story = { - name: '1:1', -}; - export const _11Blocked = (): JSX.Element => ( ); - -_11Blocked.story = { - name: '1:1 Blocked', -}; diff --git a/ts/components/conversation/conversation-details/ConversationDetailsHeader.stories.tsx b/ts/components/conversation/conversation-details/ConversationDetailsHeader.stories.tsx index c538521412b5..606fd202471a 100644 --- a/ts/components/conversation/conversation-details/ConversationDetailsHeader.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetailsHeader.stories.tsx @@ -2,17 +2,14 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; -import { number, text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; import { getFakeBadges } from '../../../test-both/helpers/getFakeBadge'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import { StorybookThemeContext } from '../../../../.storybook/StorybookThemeContext'; import type { ConversationType } from '../../../state/ducks/conversations'; - import type { Props } from './ConversationDetailsHeader'; import { ConversationDetailsHeader } from './ConversationDetailsHeader'; @@ -21,18 +18,17 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/ConversationDetailsHeader', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const createConversation = (): ConversationType => getDefaultConversation({ id: '', type: 'group', lastUpdated: 0, - title: text('conversation title', 'Some Conversation'), - groupDescription: text( - 'description', - 'This is a group description. https://www.signal.org' - ), + title: 'Some Conversation', + groupDescription: 'This is a group description. https://www.signal.org', }); function Wrapper(overrideProps: Partial) { @@ -45,7 +41,7 @@ function Wrapper(overrideProps: Partial) { i18n={i18n} canEdit={false} startEditing={action('startEditing')} - memberships={new Array(number('conversation members length', 0))} + memberships={new Array(0)} isGroup isMe={false} theme={theme} @@ -72,10 +68,6 @@ export function BasicNoDescription(): JSX.Element { ); } -BasicNoDescription.story = { - name: 'Basic no-description', -}; - export function EditableNoDescription(): JSX.Element { return ( ( - -); - -_11.story = { - name: '1:1', -}; +export function OneOnOne(): JSX.Element { + return ; +} export function NoteToSelf(): JSX.Element { return ; } - -NoteToSelf.story = { - name: 'Note to self', -}; diff --git a/ts/components/conversation/conversation-details/ConversationDetailsIcon.stories.tsx b/ts/components/conversation/conversation-details/ConversationDetailsIcon.stories.tsx index e48be8cc015a..59da2abfb8ab 100644 --- a/ts/components/conversation/conversation-details/ConversationDetailsIcon.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetailsIcon.stories.tsx @@ -2,15 +2,14 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import type { Props } from './ConversationDetailsIcon'; import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon'; export default { title: 'Components/Conversation/ConversationDetails/ConversationDetailIcon', -}; +} satisfies Meta; const createProps = (overrideProps: Partial): Props => ({ ariaLabel: overrideProps.ariaLabel || '', diff --git a/ts/components/conversation/conversation-details/ConversationDetailsMediaList.stories.tsx b/ts/components/conversation/conversation-details/ConversationDetailsMediaList.stories.tsx index 41487341be62..0ba75c510047 100644 --- a/ts/components/conversation/conversation-details/ConversationDetailsMediaList.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetailsMediaList.stories.tsx @@ -2,26 +2,24 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; - import type { Props } from './ConversationDetailsMediaList'; import { ConversationDetailsMediaList } from './ConversationDetailsMediaList'; +import type { MediaItemType } from '../../../types/MediaItem'; +import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; import { createPreparedMediaItems, createRandomMedia, -} from '../media-gallery/AttachmentSection.stories'; -import type { MediaItemType } from '../../../types/MediaItem'; -import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; +} from '../media-gallery/utils/mocks'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/ConversationMediaList', -}; +} satisfies Meta; const createProps = (mediaItems?: Array): Props => ({ conversation: getDefaultConversation({ diff --git a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.stories.tsx b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.stories.tsx index 099fba8be96e..c2a5ef6a0fb8 100644 --- a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.stories.tsx @@ -2,11 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { isBoolean } from 'lodash'; - import { action } from '@storybook/addon-actions'; -import { number } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; @@ -23,14 +20,23 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/ConversationDetailsMembershipList', -}; + argTypes: {}, + args: { + canAddNewMembers: false, + conversationId: '123', + getPreferredBadge: () => undefined, + i18n, + memberships: [], + showContactModal: action('showContactModal'), + startAddingNewMembers: action('startAddingNewMembers'), + theme: ThemeType.light, + }, +} satisfies Meta; const createMemberships = ( numberOfMemberships = 10 ): Array => { - return Array.from( - new Array(number('number of memberships', numberOfMemberships)) - ).map( + return Array.from(new Array(numberOfMemberships)).map( (_, i): GroupV2Membership => ({ isAdmin: i % 3 === 0, member: getDefaultConversation({ @@ -40,81 +46,52 @@ const createMemberships = ( ); }; -const createProps = (overrideProps: Partial): Props => ({ - canAddNewMembers: isBoolean(overrideProps.canAddNewMembers) - ? overrideProps.canAddNewMembers - : false, - conversationId: '123', - getPreferredBadge: () => undefined, - i18n, - memberships: overrideProps.memberships || [], - showContactModal: action('showContactModal'), - startAddingNewMembers: action('startAddingNewMembers'), - theme: ThemeType.light, -}); - -export function Few(): JSX.Element { +export function Few(args: Props): JSX.Element { const memberships = createMemberships(3); - - const props = createProps({ memberships }); - - return ; + return ( + + ); } -export function Limit(): JSX.Element { +export function Limit(args: Props): JSX.Element { const memberships = createMemberships(5); - - const props = createProps({ memberships }); - - return ; + return ( + + ); } -export function Limit1(): JSX.Element { +export function Limit1(args: Props): JSX.Element { const memberships = createMemberships(6); - - const props = createProps({ memberships }); - - return ; + return ( + + ); } -Limit1.story = { - name: 'Limit +1', -}; - -export function Limit2(): JSX.Element { +export function Limit2(args: Props): JSX.Element { const memberships = createMemberships(7); - - const props = createProps({ memberships }); - - return ; + return ( + + ); } -Limit2.story = { - name: 'Limit +2', -}; - -export function Many(): JSX.Element { +export function Many(args: Props): JSX.Element { const memberships = createMemberships(100); - - const props = createProps({ memberships }); - - return ; + return ( + + ); } -export function None(): JSX.Element { - const props = createProps({ memberships: [] }); - - return ; +export function None(args: Props): JSX.Element { + return ; } -export function CanAddNewMembers(): JSX.Element { +export function CanAddNewMembers(args: Props): JSX.Element { const memberships = createMemberships(10); - - const props = createProps({ canAddNewMembers: true, memberships }); - - return ; + return ( + + ); } - -CanAddNewMembers.story = { - name: 'Can add new members', -}; diff --git a/ts/components/conversation/conversation-details/ConversationNotificationsSettings.stories.tsx b/ts/components/conversation/conversation-details/ConversationNotificationsSettings.stories.tsx index 22c529bae36c..511cdd32b58d 100644 --- a/ts/components/conversation/conversation-details/ConversationNotificationsSettings.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationNotificationsSettings.stories.tsx @@ -2,11 +2,11 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; +import type { PropsType } from './ConversationNotificationsSettings'; import { ConversationNotificationsSettings } from './ConversationNotificationsSettings'; const i18n = setupI18n('en', enMessages); @@ -14,7 +14,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/ConversationNotificationsSettings', -}; +} satisfies Meta; const getCommonProps = () => ({ id: 'conversation-id', @@ -30,10 +30,6 @@ export function GroupConversationAllDefault(): JSX.Element { return ; } -GroupConversationAllDefault.story = { - name: 'Group conversation, all default', -}; - export function GroupConversationMuted(): JSX.Element { return ( ); } - -GroupConversationMentionsMuted.story = { - name: 'Group conversation, @mentions muted', -}; diff --git a/ts/components/conversation/conversation-details/ConversationNotificationsSettings.tsx b/ts/components/conversation/conversation-details/ConversationNotificationsSettings.tsx index 2d0b0b243eec..2c28cd76e6ac 100644 --- a/ts/components/conversation/conversation-details/ConversationNotificationsSettings.tsx +++ b/ts/components/conversation/conversation-details/ConversationNotificationsSettings.tsx @@ -14,7 +14,7 @@ import { getMuteOptions } from '../../../util/getMuteOptions'; import { parseIntOrThrow } from '../../../util/parseIntOrThrow'; import { useUniqueId } from '../../../hooks/useUniqueId'; -type PropsType = { +export type PropsType = { id: string; conversationType: ConversationTypeType; dontNotifyForMentionsIfMuted: boolean; diff --git a/ts/components/conversation/conversation-details/EditConversationAttributesModal.stories.tsx b/ts/components/conversation/conversation-details/EditConversationAttributesModal.stories.tsx index 07a13f63cf83..d6a5626ab617 100644 --- a/ts/components/conversation/conversation-details/EditConversationAttributesModal.stories.tsx +++ b/ts/components/conversation/conversation-details/EditConversationAttributesModal.stories.tsx @@ -3,9 +3,8 @@ import type { ComponentProps } from 'react'; import React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import { EditConversationAttributesModal } from './EditConversationAttributesModal'; @@ -16,7 +15,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/EditConversationAttributesModal', -}; +} satisfies Meta; type PropsType = ComponentProps; @@ -40,10 +39,6 @@ export function NoAvatarEmptyTitle(): JSX.Element { return ; } -NoAvatarEmptyTitle.story = { - name: 'No avatar, empty title', -}; - export function AvatarAndTitle(): JSX.Element { return ( ); } - -HasError.story = { - name: 'Has error', -}; diff --git a/ts/components/conversation/conversation-details/GroupLinkManagement.stories.tsx b/ts/components/conversation/conversation-details/GroupLinkManagement.stories.tsx index 6c34590e2355..501a99aa2eb0 100644 --- a/ts/components/conversation/conversation-details/GroupLinkManagement.stories.tsx +++ b/ts/components/conversation/conversation-details/GroupLinkManagement.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import type { PropsType } from './GroupLinkManagement'; @@ -17,7 +16,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/GroupLinkManagement', -}; +} satisfies Meta; const AccessControlEnum = Proto.AccessControl.AccessRequired; @@ -61,10 +60,6 @@ export function OffAdmin(): JSX.Element { return ; } -OffAdmin.story = { - name: 'Off (Admin)', -}; - export function OnAdmin(): JSX.Element { const props = createProps( getConversation('https://signal.group/1', AccessControlEnum.ANY), @@ -74,10 +69,6 @@ export function OnAdmin(): JSX.Element { return ; } -OnAdmin.story = { - name: 'On (Admin)', -}; - export function OnAdminAdminApprovalNeeded(): JSX.Element { const props = createProps( getConversation('https://signal.group/1', AccessControlEnum.ADMINISTRATOR), @@ -87,10 +78,6 @@ export function OnAdminAdminApprovalNeeded(): JSX.Element { return ; } -OnAdminAdminApprovalNeeded.story = { - name: 'On (Admin + Admin Approval Needed)', -}; - export function OnNonAdmin(): JSX.Element { const props = createProps( getConversation('https://signal.group/1', AccessControlEnum.ANY) @@ -99,16 +86,8 @@ export function OnNonAdmin(): JSX.Element { return ; } -OnNonAdmin.story = { - name: 'On (Non-admin)', -}; - export function OffNonAdminUserCannotGetHere(): JSX.Element { const props = createProps(undefined, false); return ; } - -OffNonAdminUserCannotGetHere.story = { - name: 'Off (Non-admin) - user cannot get here', -}; diff --git a/ts/components/conversation/conversation-details/GroupV2Permissions.stories.tsx b/ts/components/conversation/conversation-details/GroupV2Permissions.stories.tsx index 60ea837f9443..63c23f907cd5 100644 --- a/ts/components/conversation/conversation-details/GroupV2Permissions.stories.tsx +++ b/ts/components/conversation/conversation-details/GroupV2Permissions.stories.tsx @@ -2,9 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import type { PropsType } from './GroupV2Permissions'; @@ -16,7 +15,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/GroupV2Permissions', -}; +} satisfies Meta; const conversation: ConversationType = getDefaultConversation({ id: '', @@ -58,10 +57,6 @@ export function NotAdmin(): JSX.Element { ); } -NotAdmin.story = { - name: 'Not admin', -}; - export function AdminButNotAnnouncementReady(): JSX.Element { return ( ); } - -AdminNotAnnouncementReadyButItWasOn.story = { - name: 'Admin, not announcement ready, but it was on', -}; diff --git a/ts/components/conversation/conversation-details/PanelRow.stories.tsx b/ts/components/conversation/conversation-details/PanelRow.stories.tsx index f13e203ba6e0..c65e2bfc7eb2 100644 --- a/ts/components/conversation/conversation-details/PanelRow.stories.tsx +++ b/ts/components/conversation/conversation-details/PanelRow.stories.tsx @@ -2,74 +2,66 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; -import { boolean, text } from '@storybook/addon-knobs'; - -import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon'; +import type { Meta } from '@storybook/react'; import type { Props } from './PanelRow'; import { PanelRow } from './PanelRow'; +import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon'; export default { title: 'Components/Conversation/ConversationDetails/PanelRow', -}; - -const createProps = (overrideProps: Partial = {}): Props => ({ - icon: boolean('with icon', overrideProps.icon !== undefined) ? ( - - ) : null, - label: text('label', (overrideProps.label as string) || ''), - info: text('info', overrideProps.info || ''), - right: text('right', (overrideProps.right as string) || ''), - actions: boolean('with action', overrideProps.actions !== undefined) ? ( - - ) : null, - onClick: boolean('clickable', overrideProps.onClick !== undefined) - ? overrideProps.onClick || action('onClick') - : undefined, -}); - -export function Basic(): JSX.Element { - const props = createProps({ - label: 'this is a panel row', - }); - - return ; -} - -export function Simple(): JSX.Element { - const props = createProps({ - label: 'this is a panel row', - icon: 'with icon', - right: 'side text', - }); - - return ; -} - -export function Full(): JSX.Element { - const props = createProps({ - label: 'this is a panel row', - icon: 'with icon', - info: 'this is some info that exists below the main label', - right: 'side text', - actions: 'with action', - }); - - return ; -} - -export function Button(): JSX.Element { - const props = createProps({ - label: 'this is a panel row', - icon: 'with icon', - right: 'side text', + argTypes: {}, + args: { + icon: , + label: '', + info: '', + right: '', + actions: ( + + ), onClick: action('onClick'), - }); + }, +} satisfies Meta; - return ; +export function Basic(args: Props): JSX.Element { + return ; +} + +export function Simple(args: Props): JSX.Element { + return ( + + ); +} + +export function Full(args: Props): JSX.Element { + return ( + + ); +} + +export function Button(args: Props): JSX.Element { + return ( + + ); } diff --git a/ts/components/conversation/conversation-details/PanelSection.stories.tsx b/ts/components/conversation/conversation-details/PanelSection.stories.tsx index 437af8106aa8..eda85a6dbc77 100644 --- a/ts/components/conversation/conversation-details/PanelSection.stories.tsx +++ b/ts/components/conversation/conversation-details/PanelSection.stories.tsx @@ -2,65 +2,46 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; -import { boolean, text } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import type { Props } from './PanelSection'; import { PanelSection } from './PanelSection'; import { PanelRow } from './PanelRow'; export default { title: 'Components/Conversation/ConversationDetails/PanelSection', -}; + argTypes: {}, + args: { + title: 'this is a panel row', + centerTitle: false, + actions: null, + }, +} satisfies Meta; -const createProps = (overrideProps: Partial = {}): Props => ({ - title: text('label', overrideProps.title || ''), - centerTitle: boolean('centerTitle', overrideProps.centerTitle || false), - actions: boolean('with action', overrideProps.actions !== undefined) ? ( - - ) : null, -}); - -export function Basic(): JSX.Element { - const props = createProps({ - title: 'panel section header', - }); - - return ; +export function Basic(args: Props): JSX.Element { + return ; } -export function Centered(): JSX.Element { - const props = createProps({ - title: 'this is a panel row', - centerTitle: true, - }); - - return ; +export function Centered(args: Props): JSX.Element { + return ; } -export function WithActions(): JSX.Element { - const props = createProps({ - title: 'this is a panel row', - actions: ( - - ), - }); - - return ; -} - -export function WithContent(): JSX.Element { - const props = createProps({ - title: 'this is a panel row', - }); - +export function WithActions(args: Props): JSX.Element { return ( - + + action + + } + /> + ); +} + +export function WithContent(args: Props): JSX.Element { + return ( + diff --git a/ts/components/conversation/conversation-details/PendingInvites.stories.tsx b/ts/components/conversation/conversation-details/PendingInvites.stories.tsx index 140acd19c830..069b7a91b046 100644 --- a/ts/components/conversation/conversation-details/PendingInvites.stories.tsx +++ b/ts/components/conversation/conversation-details/PendingInvites.stories.tsx @@ -3,9 +3,8 @@ import * as React from 'react'; import { times } from 'lodash'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { generateAci } from '../../../types/ServiceId'; import { StorySendMode } from '../../../types/Stories'; import { setupI18n } from '../../../util/setupI18n'; @@ -21,7 +20,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/ConversationDetails/PendingInvites', -}; +} satisfies Meta; const sortedGroupMembers = Array.from(Array(32)).map((_, i) => i === 0 @@ -90,7 +89,3 @@ export function WithBadges(): JSX.Element { return ; } - -WithBadges.story = { - name: 'With badges', -}; diff --git a/ts/components/conversation/media-gallery/AttachmentSection.stories.tsx b/ts/components/conversation/media-gallery/AttachmentSection.stories.tsx index 5d30e734cd4c..f69a850f1cce 100644 --- a/ts/components/conversation/media-gallery/AttachmentSection.stories.tsx +++ b/ts/components/conversation/media-gallery/AttachmentSection.stories.tsx @@ -4,122 +4,45 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import * as React from 'react'; -import { select, text } from '@storybook/addon-knobs'; -import { random, range, sample, sortBy } from 'lodash'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; -import type { MIMEType } from '../../../types/MIME'; -import type { MediaItemType } from '../../../types/MediaItem'; - import type { Props } from './AttachmentSection'; import { AttachmentSection } from './AttachmentSection'; +import { createRandomDocuments, createRandomMedia, days } from './utils/mocks'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/MediaGallery/AttachmentSection', -}; - -export const now = Date.now(); -const DAY_MS = 24 * 60 * 60 * 1000; -export const days = (n: number) => n * DAY_MS; -const tokens = ['foo', 'bar', 'baz', 'qux', 'quux']; - -const contentTypes = { - gif: 'image/gif', - jpg: 'image/jpeg', - png: 'image/png', - mp4: 'video/mp4', - docx: 'application/text', - pdf: 'application/pdf', - txt: 'application/text', -} as unknown as Record; - -const createRandomFile = ( - startTime: number, - timeWindow: number, - fileExtension: string -): MediaItemType => { - const contentType = contentTypes[fileExtension]; - const fileName = `${sample(tokens)}${sample(tokens)}.${fileExtension}`; - - return { - contentType, - message: { - conversationId: '123', - id: random(now).toString(), - received_at: Math.floor(Math.random() * 10), - received_at_ms: random(startTime, startTime + timeWindow), - attachments: [], - sent_at: Date.now(), + component: AttachmentSection, + argTypes: { + header: { control: { type: 'text' } }, + type: { + control: { + type: 'select', + options: ['media', 'documents'], + }, }, - attachment: { - url: '', - fileName, - size: random(1000, 1000 * 1000 * 50), - contentType, - }, - index: 0, - thumbnailObjectUrl: `https://placekitten.com/${random(50, 150)}/${random( - 50, - 150 - )}`, - }; -}; + }, + args: { + i18n, + header: 'Today', + type: 'media', + mediaItems: [], + onItemClick: action('onItemClick'), + }, +} satisfies Meta; -const createRandomFiles = ( - startTime: number, - timeWindow: number, - fileExtensions: Array -) => - range(random(5, 10)).map(() => - createRandomFile(startTime, timeWindow, sample(fileExtensions) as string) +export function Documents(args: Props) { + const mediaItems = createRandomDocuments(Date.now(), days(1)); + return ( + ); - -export const createRandomDocuments = (startTime: number, timeWindow: number) => - createRandomFiles(startTime, timeWindow, ['docx', 'pdf', 'txt']); - -export const createRandomMedia = (startTime: number, timeWindow: number) => - createRandomFiles(startTime, timeWindow, ['mp4', 'jpg', 'png', 'gif']); - -export const createPreparedMediaItems = ( - fn: typeof createRandomDocuments | typeof createRandomMedia -) => - sortBy( - [ - ...fn(now, days(1)), - ...fn(now - days(1), days(1)), - ...fn(now - days(3), days(3)), - ...fn(now - days(30), days(15)), - ...fn(now - days(365), days(300)), - ], - (item: MediaItemType) => -item.message.received_at - ); - -const createProps = (overrideProps: Partial = {}): Props => ({ - i18n, - header: text('header', 'Today'), - type: select( - 'type', - { media: 'media', documents: 'documents' }, - overrideProps.type || 'media' - ), - mediaItems: overrideProps.mediaItems || [], - onItemClick: action('onItemClick'), -}); - -export function Documents() { - const mediaItems = createRandomDocuments(now, days(1)); - const props = createProps({ mediaItems, type: 'documents' }); - - return ; } -export function Media() { - const mediaItems = createRandomMedia(now, days(1)); - const props = createProps({ mediaItems, type: 'media' }); - - return ; +export function Media(args: Props) { + const mediaItems = createRandomMedia(Date.now(), days(1)); + return ; } diff --git a/ts/components/conversation/media-gallery/DocumentListItem.stories.tsx b/ts/components/conversation/media-gallery/DocumentListItem.stories.tsx index 6e16b96dd0d0..46646c78075c 100644 --- a/ts/components/conversation/media-gallery/DocumentListItem.stories.tsx +++ b/ts/components/conversation/media-gallery/DocumentListItem.stories.tsx @@ -2,25 +2,30 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { boolean, date, number, text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { Props } from './DocumentListItem'; import { DocumentListItem } from './DocumentListItem'; export default { title: 'Components/Conversation/MediaGallery/DocumentListItem', -}; + argTypes: { + timestamp: { control: { type: 'date' } }, + fileName: { control: { type: 'text' } }, + fileSize: { control: { type: 'number' } }, + shouldShowSeparator: { control: { type: 'boolean' } }, + }, + args: { + timestamp: Date.now(), + fileName: 'meow.jpg', + fileSize: 1024 * 1000 * 2, + shouldShowSeparator: false, + onClick: action('onClick'), + }, +} satisfies Meta; -export function Single(): JSX.Element { - return ( - - ); +export function Single(args: Props): JSX.Element { + return ; } export function Multiple(): JSX.Element { diff --git a/ts/components/conversation/media-gallery/DocumentListItem.tsx b/ts/components/conversation/media-gallery/DocumentListItem.tsx index 77fdc07dc62b..8975aebae990 100644 --- a/ts/components/conversation/media-gallery/DocumentListItem.tsx +++ b/ts/components/conversation/media-gallery/DocumentListItem.tsx @@ -7,7 +7,7 @@ import classNames from 'classnames'; import moment from 'moment'; import formatFileSize from 'filesize'; -type Props = { +export type Props = { // Required timestamp: number; diff --git a/ts/components/conversation/media-gallery/EmptyState.stories.tsx b/ts/components/conversation/media-gallery/EmptyState.stories.tsx index a02c7fe5dd5d..4031462679b1 100644 --- a/ts/components/conversation/media-gallery/EmptyState.stories.tsx +++ b/ts/components/conversation/media-gallery/EmptyState.stories.tsx @@ -2,13 +2,20 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { text } from '@storybook/addon-knobs'; +import type { Meta } from '@storybook/react'; +import type { Props } from './EmptyState'; import { EmptyState } from './EmptyState'; export default { title: 'Components/Conversation/MediaGallery/EmptyState', -}; + argTypes: { + label: { control: { type: 'text' } }, + }, + args: { + label: 'placeholder text', + }, +} satisfies Meta; -export function Default(): JSX.Element { - return ; +export function Default(args: Props): JSX.Element { + return ; } diff --git a/ts/components/conversation/media-gallery/EmptyState.tsx b/ts/components/conversation/media-gallery/EmptyState.tsx index 4415442c39b7..56829fb377a1 100644 --- a/ts/components/conversation/media-gallery/EmptyState.tsx +++ b/ts/components/conversation/media-gallery/EmptyState.tsx @@ -3,7 +3,7 @@ import React from 'react'; -type Props = { +export type Props = { label: string; }; diff --git a/ts/components/conversation/media-gallery/MediaGallery.stories.tsx b/ts/components/conversation/media-gallery/MediaGallery.stories.tsx index 1027f61a7624..2c5c9182ee01 100644 --- a/ts/components/conversation/media-gallery/MediaGallery.stories.tsx +++ b/ts/components/conversation/media-gallery/MediaGallery.stories.tsx @@ -3,25 +3,23 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; - +import type { Props } from './MediaGallery'; +import { MediaGallery } from './MediaGallery'; import { createPreparedMediaItems, createRandomDocuments, createRandomMedia, days, - now, -} from './AttachmentSection.stories'; -import type { Props } from './MediaGallery'; -import { MediaGallery } from './MediaGallery'; +} from './utils/mocks'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/MediaGallery/MediaGallery', -}; +} satisfies Meta; const createProps = (overrideProps: Partial = {}): Props => ({ conversationId: '123', @@ -34,7 +32,7 @@ const createProps = (overrideProps: Partial = {}): Props => ({ }); export function Populated(): JSX.Element { - const documents = createRandomDocuments(now, days(1)).slice(0, 1); + const documents = createRandomDocuments(Date.now(), days(1)).slice(0, 1); const media = createPreparedMediaItems(createRandomMedia); const props = createProps({ documents, media }); @@ -56,8 +54,8 @@ export function NoMedia(): JSX.Element { } export function OneEach(): JSX.Element { - const media = createRandomMedia(now, days(1)).slice(0, 1); - const documents = createRandomDocuments(now, days(1)).slice(0, 1); + const media = createRandomMedia(Date.now(), days(1)).slice(0, 1); + const documents = createRandomDocuments(Date.now(), days(1)).slice(0, 1); const props = createProps({ documents, media }); diff --git a/ts/components/conversation/media-gallery/MediaGridItem.stories.tsx b/ts/components/conversation/media-gallery/MediaGridItem.stories.tsx index 7f1622044291..10672f0bfcac 100644 --- a/ts/components/conversation/media-gallery/MediaGridItem.stories.tsx +++ b/ts/components/conversation/media-gallery/MediaGridItem.stories.tsx @@ -3,13 +3,12 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import type { MediaItemType } from '../../../types/MediaItem'; import type { AttachmentType } from '../../../types/Attachment'; import { stringToMIMEType } from '../../../types/MIME'; - import type { Props } from './MediaGridItem'; import { MediaGridItem } from './MediaGridItem'; @@ -17,7 +16,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Conversation/MediaGallery/MediaGridItem', -}; +} satisfies Meta; const createProps = ( overrideProps: Partial & { mediaItem: MediaItemType } @@ -131,7 +130,3 @@ export function OtherContentType(): JSX.Element { return ; } - -OtherContentType.story = { - name: 'Other ContentType', -}; diff --git a/ts/components/conversation/media-gallery/utils/mocks.ts b/ts/components/conversation/media-gallery/utils/mocks.ts new file mode 100644 index 000000000000..4894de4ad85b --- /dev/null +++ b/ts/components/conversation/media-gallery/utils/mocks.ts @@ -0,0 +1,91 @@ +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { random, range, sample, sortBy } from 'lodash'; +import type { MIMEType } from '../../../../types/MIME'; +import type { MediaItemType } from '../../../../types/MediaItem'; + +const DAY_MS = 24 * 60 * 60 * 1000; +export const days = (n: number): number => n * DAY_MS; +const tokens = ['foo', 'bar', 'baz', 'qux', 'quux']; + +const contentTypes = { + gif: 'image/gif', + jpg: 'image/jpeg', + png: 'image/png', + mp4: 'video/mp4', + docx: 'application/text', + pdf: 'application/pdf', + txt: 'application/text', +} as unknown as Record; + +function createRandomFile( + startTime: number, + timeWindow: number, + fileExtension: string +): MediaItemType { + const contentType = contentTypes[fileExtension]; + const fileName = `${sample(tokens)}${sample(tokens)}.${fileExtension}`; + + return { + contentType, + message: { + conversationId: '123', + id: random(Date.now()).toString(), + received_at: Math.floor(Math.random() * 10), + received_at_ms: random(startTime, startTime + timeWindow), + attachments: [], + sent_at: Date.now(), + }, + attachment: { + url: '', + fileName, + size: random(1000, 1000 * 1000 * 50), + contentType, + }, + index: 0, + thumbnailObjectUrl: `https://placekitten.com/${random(50, 150)}/${random( + 50, + 150 + )}`, + }; +} + +function createRandomFiles( + startTime: number, + timeWindow: number, + fileExtensions: Array +): Array { + return range(random(5, 10)).map(() => + createRandomFile(startTime, timeWindow, sample(fileExtensions) as string) + ); +} +export function createRandomDocuments( + startTime: number, + timeWindow: number +): Array { + return createRandomFiles(startTime, timeWindow, ['docx', 'pdf', 'txt']); +} + +export function createRandomMedia( + startTime: number, + timeWindow: number +): Array { + return createRandomFiles(startTime, timeWindow, ['mp4', 'jpg', 'png', 'gif']); +} + +export function createPreparedMediaItems( + fn: typeof createRandomDocuments | typeof createRandomMedia +): Array { + const now = Date.now(); + return sortBy( + [ + ...fn(now, days(1)), + ...fn(now - days(1), days(1)), + ...fn(now - days(3), days(3)), + ...fn(now - days(30), days(15)), + ...fn(now - days(365), days(300)), + ], + (item: MediaItemType) => -item.message.received_at + ); +} diff --git a/ts/components/conversationList/MessageSearchResult.stories.tsx b/ts/components/conversationList/MessageSearchResult.stories.tsx index 8401f31cc0fa..d56e3d5868d3 100644 --- a/ts/components/conversationList/MessageSearchResult.stories.tsx +++ b/ts/components/conversationList/MessageSearchResult.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import { StorybookThemeContext } from '../../../.storybook/StorybookThemeContext'; @@ -23,7 +23,7 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/MessageSearchResult', -}; +} satisfies Meta; const someone = getDefaultConversation({ title: 'Some Person', @@ -85,10 +85,6 @@ export function SenderHasABadge(): JSX.Element { return ; } -SenderHasABadge.story = { - name: 'Sender has a badge', -}; - export function Selected(): JSX.Element { const props = useProps({ from: someone, @@ -118,10 +114,6 @@ export function SearchingInConversation(): JSX.Element { return ; } -SearchingInConversation.story = { - name: 'Searching in Conversation', -}; - export function FromYouToYourself(): JSX.Element { const props = useProps({ from: me, @@ -131,10 +123,6 @@ export function FromYouToYourself(): JSX.Element { return ; } -FromYouToYourself.story = { - name: 'From You to Yourself', -}; - export function FromYouToGroup(): JSX.Element { const props = useProps({ from: me, @@ -144,10 +132,6 @@ export function FromYouToGroup(): JSX.Element { return ; } -FromYouToGroup.story = { - name: 'From You to Group', -}; - export function FromSomeoneToGroup(): JSX.Element { const props = useProps({ from: someone, @@ -157,10 +141,6 @@ export function FromSomeoneToGroup(): JSX.Element { return ; } -FromSomeoneToGroup.story = { - name: 'From Someone to Group', -}; - export function LongSearchResult(): JSX.Element { const snippets = [ 'This is a really <>detail<>ed long line which will wrap and only be cut off after it gets to three lines. So maybe this will make it in as well?', @@ -193,10 +173,6 @@ export function EmptyShouldBeInvalid(): JSX.Element { return ; } -EmptyShouldBeInvalid.story = { - name: 'Empty (should be invalid)', -}; - export function Mention(): JSX.Element { const props = useProps({ body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC', @@ -225,10 +201,6 @@ export function Mention(): JSX.Element { return ; } -Mention.story = { - name: '@mention', -}; - export function MentionRegexp(): JSX.Element { const props = useProps({ body: '\uFFFC This is a (long) /text/ ^$ that is ... specially **crafted** to (test) our regexp escaping mechanism! Making sure that the code we write works in all sorts of scenarios', @@ -250,10 +222,6 @@ export function MentionRegexp(): JSX.Element { return ; } -MentionRegexp.story = { - name: '@mention regexp', -}; - export function MentionNoMatches(): JSX.Element { const props = useProps({ body: '\uFFFC hello', @@ -274,10 +242,6 @@ export function MentionNoMatches(): JSX.Element { return ; } -MentionNoMatches.story = { - name: '@mention no-matches', -}; - export const _MentionNoMatches = (): JSX.Element => { const props = useProps({ body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC', @@ -306,10 +270,6 @@ export const _MentionNoMatches = (): JSX.Element => { return ; }; -_MentionNoMatches.story = { - name: '@mention no-matches', -}; - export function DoubleMention(): JSX.Element { const props = useProps({ body: 'Hey \uFFFC \uFFFC --- test! Two mentions!', @@ -337,10 +297,6 @@ export function DoubleMention(): JSX.Element { return ; } -DoubleMention.story = { - name: 'Double @mention', -}; - export function WithFormatting(): JSX.Element { const props = useProps({ body: "We're playing with formatting in fun ways like you do!", diff --git a/ts/components/emoji/Emoji.stories.tsx b/ts/components/emoji/Emoji.stories.tsx index c61d25229c69..403f64ba37fe 100644 --- a/ts/components/emoji/Emoji.stories.tsx +++ b/ts/components/emoji/Emoji.stories.tsx @@ -2,63 +2,58 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { select, text } from '@storybook/addon-knobs'; +import type { Meta } from '@storybook/react'; import type { Props } from './Emoji'; import { Emoji, EmojiSizes } from './Emoji'; -export default { - title: 'Components/Emoji/Emoji', -}; - const tones = [0, 1, 2, 3, 4, 5]; -const createProps = (overrideProps: Partial = {}): Props => ({ - size: select( - 'size', - EmojiSizes.reduce((m, t) => ({ ...m, [t]: t }), {}), - overrideProps.size || 48 - ), - emoji: text('emoji', overrideProps.emoji || ''), - shortName: text('shortName', overrideProps.shortName || ''), - skinTone: select( - 'skinTone', - tones.reduce((m, t) => ({ ...m, [t]: t }), {}), - overrideProps.skinTone || 0 - ), -}); - -export function Sizes(): JSX.Element { - const props = createProps({ - shortName: 'grinning_face_with_star_eyes', - }); +export default { + title: 'Components/Emoji/Emoji', + argTypes: { + size: { control: { type: 'select' }, options: EmojiSizes }, + emoji: { control: { type: 'text' } }, + shortName: { control: { type: 'text' } }, + skinTone: { control: { type: 'select' }, options: tones }, + }, + args: { + size: 48, + emoji: '', + shortName: '', + skinTone: 0, + }, +} satisfies Meta; +export function Sizes(args: Props): JSX.Element { return ( <> {EmojiSizes.map(size => ( - + ))} ); } -export function SkinTones(): JSX.Element { - const props = createProps({ - shortName: 'raised_back_of_hand', - }); - +export function SkinTones(args: Props): JSX.Element { return ( <> {tones.map(skinTone => ( - + ))} ); } -export function FromEmoji(): JSX.Element { - const props = createProps({ - emoji: '😂', - }); - - return ; +export function FromEmoji(args: Props): JSX.Element { + return ; } diff --git a/ts/components/emoji/EmojiButton.stories.tsx b/ts/components/emoji/EmojiButton.stories.tsx index 7d656e51002f..72a890262f00 100644 --- a/ts/components/emoji/EmojiButton.stories.tsx +++ b/ts/components/emoji/EmojiButton.stories.tsx @@ -2,19 +2,18 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; - +import type { Props } from './EmojiButton'; import { EmojiButton } from './EmojiButton'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Emoji/EmojiButton', -}; +} satisfies Meta; export function Base(): JSX.Element { return ( diff --git a/ts/components/emoji/EmojiPicker.stories.tsx b/ts/components/emoji/EmojiPicker.stories.tsx index 4014f95a4b3f..3616aed7e8e7 100644 --- a/ts/components/emoji/EmojiPicker.stories.tsx +++ b/ts/components/emoji/EmojiPicker.stories.tsx @@ -2,19 +2,18 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; - +import type { Props } from './EmojiPicker'; import { EmojiPicker } from './EmojiPicker'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Emoji/EmojiPicker', -}; +} satisfies Meta; export function Base(): JSX.Element { return ( @@ -88,7 +87,3 @@ export function WithSettingsButton(): JSX.Element { /> ); } - -WithSettingsButton.story = { - name: 'With settings button', -}; diff --git a/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.stories.tsx b/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.stories.tsx index 72d46649dcd6..5faff622816a 100644 --- a/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.stories.tsx +++ b/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.stories.tsx @@ -2,19 +2,18 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { useState } from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; - +import type { PropsType } from './InstallScreenChoosingDeviceNameStep'; import { InstallScreenChoosingDeviceNameStep } from './InstallScreenChoosingDeviceNameStep'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/InstallScreen/InstallScreenChoosingDeviceNameStep', -}; +} satisfies Meta; function Wrapper() { const [deviceName, setDeviceName] = useState('Default value'); diff --git a/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx b/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx index 7d5f99bcb7db..09b48a7e9cc0 100644 --- a/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx +++ b/ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx @@ -16,7 +16,7 @@ import { InstallScreenSignalLogo } from './InstallScreenSignalLogo'; // DESKTOP-2844. export const MAX_DEVICE_NAME_LENGTH = 50; -type PropsType = { +export type PropsType = { deviceName: string; i18n: LocalizerType; onSubmit: () => void; diff --git a/ts/components/installScreen/InstallScreenErrorStep.stories.tsx b/ts/components/installScreen/InstallScreenErrorStep.stories.tsx index 556622938764..4cddc8307b9a 100644 --- a/ts/components/installScreen/InstallScreenErrorStep.stories.tsx +++ b/ts/components/installScreen/InstallScreenErrorStep.stories.tsx @@ -2,19 +2,18 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; - +import type { Props } from './InstallScreenErrorStep'; import { InstallScreenErrorStep, InstallError } from './InstallScreenErrorStep'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/InstallScreen/InstallScreenErrorStep', -}; +} satisfies Meta; const defaultProps = { i18n, @@ -29,26 +28,14 @@ export const _TooManyDevices = (): JSX.Element => ( /> ); -_TooManyDevices.story = { - name: 'Too many devices', -}; - export const _TooOld = (): JSX.Element => ( ); -_TooOld.story = { - name: 'Too old', -}; - export const __TooOld = (): JSX.Element => ( ); -__TooOld.story = { - name: 'Too old', -}; - export const _ConnectionFailed = (): JSX.Element => ( ( /> ); -_ConnectionFailed.story = { - name: 'Connection failed', -}; - export const _UnknownError = (): JSX.Element => ( ); - -_UnknownError.story = { - name: 'Unknown error', -}; diff --git a/ts/components/installScreen/InstallScreenErrorStep.tsx b/ts/components/installScreen/InstallScreenErrorStep.tsx index a4edcdac74ce..ff1c0184d4a2 100644 --- a/ts/components/installScreen/InstallScreenErrorStep.tsx +++ b/ts/components/installScreen/InstallScreenErrorStep.tsx @@ -19,17 +19,19 @@ export enum InstallError { QRCodeFailed, } +export type Props = Readonly<{ + error: InstallError; + i18n: LocalizerType; + quit: () => unknown; + tryAgain: () => unknown; +}>; + export function InstallScreenErrorStep({ error, i18n, quit, tryAgain, -}: Readonly<{ - error: InstallError; - i18n: LocalizerType; - quit: () => unknown; - tryAgain: () => unknown; -}>): ReactElement { +}: Props): ReactElement { let errorMessage: string; let buttonText = i18n('icu:installTryAgain'); let onClickButton = () => tryAgain(); diff --git a/ts/components/installScreen/InstallScreenLinkInProgress.stories.tsx b/ts/components/installScreen/InstallScreenLinkInProgress.stories.tsx index 56f94957a8e4..f42792ec2723 100644 --- a/ts/components/installScreen/InstallScreenLinkInProgress.stories.tsx +++ b/ts/components/installScreen/InstallScreenLinkInProgress.stories.tsx @@ -2,17 +2,17 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; - +import type { Props } from './InstallScreenLinkInProgressStep'; import { InstallScreenLinkInProgressStep } from './InstallScreenLinkInProgressStep'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/InstallScreen/InstallScreenLinkInProgressStep', -}; +} satisfies Meta; export function Default(): JSX.Element { return ; diff --git a/ts/components/installScreen/InstallScreenLinkInProgressStep.tsx b/ts/components/installScreen/InstallScreenLinkInProgressStep.tsx index a56a5fff8e8f..ebf2c2e3612e 100644 --- a/ts/components/installScreen/InstallScreenLinkInProgressStep.tsx +++ b/ts/components/installScreen/InstallScreenLinkInProgressStep.tsx @@ -10,9 +10,9 @@ import { Spinner } from '../Spinner'; import { TitlebarDragArea } from '../TitlebarDragArea'; import { InstallScreenSignalLogo } from './InstallScreenSignalLogo'; -export function InstallScreenLinkInProgressStep({ - i18n, -}: Readonly<{ i18n: LocalizerType }>): ReactElement { +export type Props = Readonly<{ i18n: LocalizerType }>; + +export function InstallScreenLinkInProgressStep({ i18n }: Props): ReactElement { return (
diff --git a/ts/components/installScreen/InstallScreenQrCodeNotScannedStep.stories.tsx b/ts/components/installScreen/InstallScreenQrCodeNotScannedStep.stories.tsx index 3610be4d7794..95edd9750006 100644 --- a/ts/components/installScreen/InstallScreenQrCodeNotScannedStep.stories.tsx +++ b/ts/components/installScreen/InstallScreenQrCodeNotScannedStep.stories.tsx @@ -3,13 +3,13 @@ import React, { useEffect, useState } from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta, StoryFn } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import { DialogType } from '../../types/Dialogs'; import enMessages from '../../../_locales/en/messages.json'; - import type { Loadable } from '../../util/loadable'; import { LoadingState } from '../../util/loadable'; +import type { PropsType } from './InstallScreenQrCodeNotScannedStep'; import { InstallScreenQrCodeNotScannedStep } from './InstallScreenQrCodeNotScannedStep'; const i18n = setupI18n('en', enMessages); @@ -32,7 +32,7 @@ const DEFAULT_UPDATES = { export default { title: 'Components/InstallScreen/InstallScreenQrCodeNotScannedStep', argTypes: {}, -}; +} satisfies Meta; function Simulation({ finalResult }: { finalResult: Loadable }) { const [provisioningUrl, setProvisioningUrl] = useState>({ @@ -77,10 +77,6 @@ export function QrCodeLoading(): JSX.Element { ); } -QrCodeLoading.story = { - name: 'QR code loading', -}; - export function QrCodeFailedToLoad(): JSX.Element { return ( ; } -SimulatedLoading.story = { - name: 'Simulated loading', -}; - export function SimulatedFailure(): JSX.Element { return ( = + // eslint-disable-next-line react/function-component-definition + function WithUpdateKnobs({ + dialogType, + currentVersion, + }: { + dialogType: DialogType; + currentVersion: string; + }): JSX.Element { + return ( + + ); + }; -export function WithUpdateKnobs({ - dialogType, - currentVersion, -}: { - dialogType: DialogType; - currentVersion: string; -}): JSX.Element { - return ( - - ); -} - -WithUpdateKnobs.story = { - name: 'With Update Knobs', - argTypes: { - dialogType: { - control: { type: 'select' }, - defaultValue: DialogType.AutoUpdate, - options: Object.values(DialogType), - }, - currentVersion: { - control: { type: 'select' }, - defaultValue: 'v6.0.0', - options: ['v6.0.0', 'v6.1.0-beta.1'], - }, +WithUpdateKnobs.argTypes = { + dialogType: { + control: { type: 'select' }, + options: Object.values(DialogType), + }, + currentVersion: { + control: { type: 'select' }, + options: ['v6.0.0', 'v6.1.0-beta.1'], }, }; +WithUpdateKnobs.args = { + dialogType: DialogType.AutoUpdate, + currentVersion: 'v6.0.0', +}; diff --git a/ts/components/installScreen/InstallScreenQrCodeNotScannedStep.tsx b/ts/components/installScreen/InstallScreenQrCodeNotScannedStep.tsx index 011b561eecb6..ee60864839a8 100644 --- a/ts/components/installScreen/InstallScreenQrCodeNotScannedStep.tsx +++ b/ts/components/installScreen/InstallScreenQrCodeNotScannedStep.tsx @@ -23,7 +23,7 @@ import { Environment, getEnvironment } from '../../environment'; // We can't always use destructuring assignment because of the complexity of this props // type. -type PropsType = Readonly<{ +export type PropsType = Readonly<{ i18n: LocalizerType; provisioningUrl: Loadable; hasExpired?: boolean; diff --git a/ts/components/stickers/StickerButton.stories.tsx b/ts/components/stickers/StickerButton.stories.tsx index 827680f32118..b8c2ac50f511 100644 --- a/ts/components/stickers/StickerButton.stories.tsx +++ b/ts/components/stickers/StickerButton.stories.tsx @@ -1,11 +1,9 @@ // Copyright 2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { DecoratorFunction } from '@storybook/addons'; import * as React from 'react'; import { action } from '@storybook/addon-actions'; -import { boolean } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { Props } from './StickerButton'; @@ -16,7 +14,7 @@ import { sticker2, tallSticker, wideSticker, -} from './StickerPicker.stories'; +} from './mocks'; const i18n = setupI18n('en', enMessages); @@ -36,8 +34,27 @@ export default { {storyFn()}
), - ] as Array>, -}; + ], + argTypes: { + showIntroduction: { control: { type: 'boolean' } }, + showPickerHint: { control: { type: 'boolean' } }, + }, + args: { + blessedPacks: [], + clearInstalledStickerPack: action('clearInstalledStickerPack'), + clearShowIntroduction: action('clearShowIntroduction'), + clearShowPickerHint: action('clearShowPickerHint'), + i18n, + installedPacks: [], + knownPacks: [], + onClickAddPack: action('onClickAddPack'), + onPickSticker: action('onPickSticker'), + receivedPacks: [], + recentStickers: [], + showIntroduction: false, + showPickerHint: false, + }, +} satisfies Meta; const receivedPacks = [ createPack({ id: 'received-pack-1', status: 'downloaded' }, sticker1), @@ -65,100 +82,61 @@ const knownPacks = [ createPack({ id: 'known-pack-2', status: 'known' }, sticker2), ]; -const createProps = (overrideProps: Partial = {}): Props => ({ - blessedPacks: overrideProps.blessedPacks || [], - clearInstalledStickerPack: action('clearInstalledStickerPack'), - clearShowIntroduction: action('clearShowIntroduction'), - clearShowPickerHint: action('clearShowPickerHint'), - i18n, - installedPack: overrideProps.installedPack, - installedPacks: overrideProps.installedPacks || [], - knownPacks: overrideProps.knownPacks || [], - onClickAddPack: action('onClickAddPack'), - onPickSticker: action('onPickSticker'), - receivedPacks: overrideProps.receivedPacks || [], - recentStickers: [], - showIntroduction: boolean( - 'showIntroduction', - overrideProps.showIntroduction || false - ), - showPickerHint: boolean('showPickerHint', false), -}); - -export function OnlyInstalled(): JSX.Element { - const props = createProps({ installedPacks }); - - return ; +export function OnlyInstalled(args: Props): JSX.Element { + return ; } -export function OnlyReceived(): JSX.Element { - const props = createProps({ receivedPacks }); - - return ; +export function OnlyReceived(args: Props): JSX.Element { + return ; } -export function OnlyKnown(): JSX.Element { - const props = createProps({ knownPacks }); - - return ; +export function OnlyKnown(args: Props): JSX.Element { + return ; } -export function OnlyBlessed(): JSX.Element { - const props = createProps({ blessedPacks }); - - return ; +export function OnlyBlessed(args: Props): JSX.Element { + return ; } -export function NoPacks(): JSX.Element { - const props = createProps(); - - return ; +export function NoPacks(args: Props): JSX.Element { + return ; } -export function InstalledPackTooltip(): JSX.Element { - const props = createProps({ - installedPacks, - installedPack: installedPacks[0], - }); - - return ; +export function InstalledPackTooltip(args: Props): JSX.Element { + return ( + + ); } -export function InstalledPackTooltipWide(): JSX.Element { +export function InstalledPackTooltipWide(args: Props): JSX.Element { const installedPack = createPack({ id: 'installed-pack-wide' }, wideSticker); - const props = createProps({ - installedPacks: [installedPack], - installedPack, - }); - - return ; + return ( + + ); } -InstalledPackTooltipWide.story = { - name: 'Installed Pack Tooltip (Wide)', -}; - -export function InstalledPackTooltipTall(): JSX.Element { +export function InstalledPackTooltipTall(args: Props): JSX.Element { const installedPack = createPack({ id: 'installed-pack-tall' }, tallSticker); - - const props = createProps({ - installedPacks: [installedPack], - installedPack, - }); - - return ; + return ( + + ); } -InstalledPackTooltipTall.story = { - name: 'Installed Pack Tooltip (Tall)', -}; - -export function NewInstallTooltip(): JSX.Element { - const props = createProps({ - installedPacks, - showIntroduction: true, - }); - - return ; +export function NewInstallTooltip(args: Props): JSX.Element { + return ( + + ); } diff --git a/ts/components/stickers/StickerManager.stories.tsx b/ts/components/stickers/StickerManager.stories.tsx index f0067314fe04..cba68a01b1e0 100644 --- a/ts/components/stickers/StickerManager.stories.tsx +++ b/ts/components/stickers/StickerManager.stories.tsx @@ -3,18 +3,18 @@ import * as React from 'react'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { Props } from './StickerManager'; import { StickerManager } from './StickerManager'; -import { createPack, sticker1, sticker2 } from './StickerPicker.stories'; +import { createPack, sticker1, sticker2 } from './mocks'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Stickers/StickerManager', -}; +} satisfies Meta; const receivedPacks = [ createPack({ id: 'received-pack-1', status: 'downloaded' }, sticker1), @@ -72,16 +72,12 @@ export function ReceivedPacks(): JSX.Element { return ; } -export function InstalledKnownPacks(): JSX.Element { +export function InstalledAndKnownPacks(): JSX.Element { const props = createProps({ installedPacks, knownPacks }); return ; } -InstalledKnownPacks.story = { - name: 'Installed + Known Packs', -}; - export function Empty(): JSX.Element { const props = createProps(); diff --git a/ts/components/stickers/StickerPicker.stories.tsx b/ts/components/stickers/StickerPicker.stories.tsx index ac341e07a79e..f9dcbf9ec3a1 100644 --- a/ts/components/stickers/StickerPicker.stories.tsx +++ b/ts/components/stickers/StickerPicker.stories.tsx @@ -2,167 +2,77 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { sample } from 'lodash'; import { action } from '@storybook/addon-actions'; -import { boolean } from '@storybook/addon-knobs'; - +import type { Meta } from '@storybook/react'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; import type { Props } from './StickerPicker'; import { StickerPicker } from './StickerPicker'; -import type { StickerPackType, StickerType } from '../../state/ducks/stickers'; +import { abeSticker, createPack, packs, recentStickers } from './mocks'; const i18n = setupI18n('en', enMessages); export default { title: 'Components/Stickers/StickerPicker', -}; + component: StickerPicker, + argTypes: { + showPickerHint: { control: { type: 'boolean' } }, + }, + args: { + i18n, + onClickAddPack: action('onClickAddPack'), + onClose: action('onClose'), + onPickSticker: action('onPickSticker'), + packs: [], + recentStickers: [], + showPickerHint: false, + }, +} satisfies Meta; -export const sticker1: StickerType = { - id: 1, - url: '/fixtures/kitten-1-64-64.jpg', - packId: 'foo', - emoji: '', -}; - -export const sticker2: StickerType = { - id: 2, - url: '/fixtures/kitten-2-64-64.jpg', - packId: 'bar', - emoji: '', -}; - -export const sticker3: StickerType = { - id: 3, - url: '/fixtures/kitten-3-64-64.jpg', - packId: 'baz', - emoji: '', -}; - -export const abeSticker: StickerType = { - id: 4, - url: '/fixtures/512x515-thumbs-up-lincoln.webp', - packId: 'abe', - emoji: '', -}; - -export const wideSticker: StickerType = { - id: 5, - url: '/fixtures/1000x50-green.jpeg', - packId: 'wide', - emoji: '', -}; - -export const tallSticker: StickerType = { - id: 6, - url: '/fixtures/50x1000-teal.jpeg', - packId: 'tall', - emoji: '', -}; - -const choosableStickers = [sticker1, sticker2, sticker3, abeSticker]; - -export const createPack = ( - props: Partial, - sticker?: StickerType -): StickerPackType => ({ - id: '', - title: props.id ? `${props.id} title` : 'title', - key: '', - author: '', - isBlessed: false, - lastUsed: 0, - status: 'known', - cover: sticker, - stickerCount: 101, - stickers: sticker - ? Array(101) - .fill(0) - .map((_, id) => ({ ...sticker, id })) - : [], - ...props, -}); - -const packs = [ - createPack({ id: 'tall' }, tallSticker), - createPack({ id: 'wide' }, wideSticker), - ...Array(20) - .fill(0) - .map((_, n) => - createPack({ id: `pack-${n}` }, sample(choosableStickers) as StickerType) - ), -]; - -const recentStickers = [ - abeSticker, - sticker1, - sticker2, - sticker3, - tallSticker, - wideSticker, - { ...sticker2, id: 9999 }, -]; - -const createProps = (overrideProps: Partial = {}): Props => ({ - i18n, - onClickAddPack: action('onClickAddPack'), - onClose: action('onClose'), - onPickSticker: action('onPickSticker'), - packs: overrideProps.packs || [], - recentStickers: overrideProps.recentStickers || [], - showPickerHint: boolean( - 'showPickerHint', - overrideProps.showPickerHint || false - ), -}); - -export function Full(): JSX.Element { - const props = createProps({ packs, recentStickers }); - - return ; +export function Full(args: Props): JSX.Element { + return ( + + ); } -export function PickerHint(): JSX.Element { - const props = createProps({ packs, recentStickers, showPickerHint: true }); - - return ; +export function PickerHint(args: Props): JSX.Element { + return ( + + ); } -export function NoRecentStickers(): JSX.Element { - const props = createProps({ packs }); - - return ; +export function NoRecentStickers(args: Props): JSX.Element { + return ; } -export function Empty(): JSX.Element { - const props = createProps(); - - return ; +export function Empty(args: Props): JSX.Element { + return ; } -export function PendingDownload(): JSX.Element { +export function PendingDownload(args: Props): JSX.Element { const pack = createPack( { status: 'pending', stickers: [abeSticker] }, abeSticker ); - const props = createProps({ packs: [pack] }); - return ; + return ; } -export function Error(): JSX.Element { +export function Error(args: Props): JSX.Element { const pack = createPack( { status: 'error', stickers: [abeSticker] }, abeSticker ); - const props = createProps({ packs: [pack] }); - return ; + return ; } -export function NoCover(): JSX.Element { +export function NoCover(args: Props): JSX.Element { const pack = createPack({ status: 'error', stickers: [abeSticker] }); - const props = createProps({ packs: [pack] }); - - return ; + return ; } diff --git a/ts/components/stickers/StickerPreviewModal.stories.tsx b/ts/components/stickers/StickerPreviewModal.stories.tsx index 59bcb211e4df..62025cac7fa1 100644 --- a/ts/components/stickers/StickerPreviewModal.stories.tsx +++ b/ts/components/stickers/StickerPreviewModal.stories.tsx @@ -2,9 +2,9 @@ // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; -import { text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; - +import type { Meta } from '@storybook/react'; +import type { Props } from './StickerPreviewModal'; import { StickerPreviewModal } from './StickerPreviewModal'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; @@ -18,7 +18,9 @@ const i18n = setupI18n('en', enMessages); export default { title: 'Components/Stickers/StickerPreviewModal', -}; + argTypes: {}, + args: {}, +} satisfies Meta; const abeSticker = { id: -1, @@ -40,8 +42,8 @@ const tallSticker = { }; export function Full(): JSX.Element { - const title = text('title', 'Foo'); - const author = text('author', 'Foo McBarrington'); + const title = 'Foo'; + const author = 'Foo McBarrington'; const pack = { id: 'foo', @@ -76,8 +78,8 @@ export function Full(): JSX.Element { } export function JustFourStickers(): JSX.Element { - const title = text('title', 'Foo'); - const author = text('author', 'Foo McBarrington'); + const title = 'Foo'; + const author = 'Foo McBarrington'; const pack = { id: 'foo', @@ -104,10 +106,6 @@ export function JustFourStickers(): JSX.Element { ); } -JustFourStickers.story = { - name: 'Just four stickers', -}; - export function InitialDownload(): JSX.Element { return ( ); } - -PackDeleted.story = { - name: 'Pack deleted', -}; diff --git a/ts/components/stickers/mocks.ts b/ts/components/stickers/mocks.ts new file mode 100644 index 000000000000..0aa6a56c693c --- /dev/null +++ b/ts/components/stickers/mocks.ts @@ -0,0 +1,90 @@ +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { sample } from 'lodash'; +import type { StickerPackType, StickerType } from '../../state/ducks/stickers'; + +export const sticker1: StickerType = { + id: 1, + url: '/fixtures/kitten-1-64-64.jpg', + packId: 'foo', + emoji: '', +}; + +export const sticker2: StickerType = { + id: 2, + url: '/fixtures/kitten-2-64-64.jpg', + packId: 'bar', + emoji: '', +}; + +export const sticker3: StickerType = { + id: 3, + url: '/fixtures/kitten-3-64-64.jpg', + packId: 'baz', + emoji: '', +}; + +export const abeSticker: StickerType = { + id: 4, + url: '/fixtures/512x515-thumbs-up-lincoln.webp', + packId: 'abe', + emoji: '', +}; + +export const wideSticker: StickerType = { + id: 5, + url: '/fixtures/1000x50-green.jpeg', + packId: 'wide', + emoji: '', +}; + +export const tallSticker: StickerType = { + id: 6, + url: '/fixtures/50x1000-teal.jpeg', + packId: 'tall', + emoji: '', +}; + +const choosableStickers = [sticker1, sticker2, sticker3, abeSticker]; + +export const createPack = ( + props: Partial, + sticker?: StickerType +): StickerPackType => ({ + id: '', + title: props.id ? `${props.id} title` : 'title', + key: '', + author: '', + isBlessed: false, + lastUsed: 0, + status: 'known', + cover: sticker, + stickerCount: 101, + stickers: sticker + ? Array(101) + .fill(0) + .map((_, id) => ({ ...sticker, id })) + : [], + ...props, +}); + +export const packs = [ + createPack({ id: 'tall' }, tallSticker), + createPack({ id: 'wide' }, wideSticker), + ...Array(20) + .fill(0) + .map((_, n) => + createPack({ id: `pack-${n}` }, sample(choosableStickers) as StickerType) + ), +]; + +export const recentStickers = [ + abeSticker, + sticker1, + sticker2, + sticker3, + tallSticker, + wideSticker, + { ...sticker2, id: 9999 }, +]; diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index 50b15b83b20f..8cabc171525a 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -362,9 +362,13 @@ export class ConversationModel extends window.Backbone if (sealedSender === undefined) { this.set({ sealedSender: SEALED_SENDER.UNKNOWN }); } + // @ts-expect-error -- Removing legacy prop this.unset('unidentifiedDelivery'); + // @ts-expect-error -- Removing legacy prop this.unset('unidentifiedDeliveryUnrestricted'); + // @ts-expect-error -- Removing legacy prop this.unset('hasFetchedProfile'); + // @ts-expect-error -- Removing legacy prop this.unset('tokens'); this.on('change:members change:membersV2', this.fetchContacts); diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index f9c551f63964..727a49871219 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -162,11 +162,11 @@ import { handleLeaveConversation, } from './composer'; import { ReceiptType } from '../../types/Receipt'; -import { sortByMessageOrder } from '../../util/maybeForwardMessages'; import { Sound, SoundType } from '../../util/Sound'; import { canEditMessage } from '../../util/canEditMessage'; import type { ChangeNavTabActionType } from './nav'; import { CHANGE_NAV_TAB, NavTab, actions as navActions } from './nav'; +import { sortByMessageOrder } from '../../types/ForwardDraft'; // State diff --git a/ts/state/smart/ForwardMessagesModal.tsx b/ts/state/smart/ForwardMessagesModal.tsx index 1f702d86d84e..d4b7edff91e2 100644 --- a/ts/state/smart/ForwardMessagesModal.tsx +++ b/ts/state/smart/ForwardMessagesModal.tsx @@ -20,10 +20,6 @@ import { import { getIntl, getTheme, getRegionCode } from '../selectors/user'; import { getLinkPreview } from '../selectors/linkPreviews'; import { getPreferredBadgeSelector } from '../selectors/badges'; -import type { - ForwardMessageData, - MessageForwardDraft, -} from '../../util/maybeForwardMessages'; import { maybeForwardMessages } from '../../util/maybeForwardMessages'; import { maybeGrabLinkPreview, @@ -37,6 +33,10 @@ import { hydrateRanges } from '../../types/BodyRange'; import { isDownloaded } from '../../types/Attachment'; import { __DEPRECATED$getMessageById } from '../../messages/getMessageById'; import { strictAssert } from '../../util/assert'; +import type { + ForwardMessageData, + MessageForwardDraft, +} from '../../types/ForwardDraft'; function toMessageForwardDraft( props: ForwardMessagePropsType, diff --git a/ts/storybook/types.ts b/ts/storybook/types.ts new file mode 100644 index 000000000000..6af54108e3d2 --- /dev/null +++ b/ts/storybook/types.ts @@ -0,0 +1,49 @@ +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { Meta } from '@storybook/react'; +import type { InputType } from '@storybook/types'; + +/** + * Converts all optional properties to required properties that can be set to `undefined`. + */ +type Defined = { + [P in keyof Required]: T[P]; +}; + +/** + * Ensures that the exported meta object from a storybook file has the correct shape. + * + * - `component` is a React component that accepts `Props` + * - `args` has a default value for everything in `Props` + * + * ```ts + * // 1. Always export default an object with the `component`, `argTypes`, and `args` + * export default { + * component: Component, + * argsTypes: { + * propName: { control: { type: "text" } }, + * }, + * args: { + * propName: "defaultValue", + * onEvent: action("onEvent"), + * }, + * + * // 3. Always use `satisfies ComponentMeta`, never use `as` it won't help you + * } satisfies ComponentMeta + * ``` + */ +export type ComponentMeta = Meta & { + /** Ensure we're talking about the right component */ + component: React.ComponentType; + /** Ensure every prop has a default even if its just `undefined` */ + args: Defined; +}; + +export function argPresets(map: Record): InputType { + return { + control: { type: 'select' }, + options: Object.keys(map), + mapping: map, + }; +} diff --git a/ts/test-both/challenge_test.ts b/ts/test-both/challenge_test.ts index a10139e0306b..93464bcad2b2 100644 --- a/ts/test-both/challenge_test.ts +++ b/ts/test-both/challenge_test.ts @@ -32,7 +32,7 @@ describe('ChallengeHandler', () => { let challengeStatus = 'idle'; let queuesStarted: Array = []; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { storage.clear(); challengeStatus = 'idle'; queuesStarted = []; @@ -43,7 +43,7 @@ describe('ChallengeHandler', () => { }); }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.sandbox.restore(); }); @@ -123,7 +123,7 @@ describe('ChallengeHandler', () => { ); }; - it('should automatically start queue after timeout', async function test() { + it('should automatically start queue after timeout', async function (this: Mocha.Context) { const handler = await createHandler(); const one = createChallenge('1'); @@ -138,7 +138,7 @@ describe('ChallengeHandler', () => { assert.isFalse(isInStorage(one.conversationId)); }); - it('should send challenge response', async function test() { + it('should send challenge response', async function (this: Mocha.Context) { const handler = await createHandler({ autoSolve: true }); const one = createChallenge('1', { @@ -154,7 +154,7 @@ describe('ChallengeHandler', () => { assert.equal(challengeStatus, 'idle'); }); - it('should send old challenges', async function test() { + it('should send old challenges', async function (this: Mocha.Context) { const handler = await createHandler(); const challenges = [ @@ -212,7 +212,7 @@ describe('ChallengeHandler', () => { assert.deepEqual(queuesStarted, [one.conversationId]); }); - it('should not retry expired challenges', async function test() { + it('should not retry expired challenges', async function (this: Mocha.Context) { const handler = await createHandler(); const one = createChallenge('1'); @@ -238,7 +238,7 @@ describe('ChallengeHandler', () => { assert.isFalse(isInStorage(one.conversationId)); }); - it('should send challenges that matured while we were offline', async function test() { + it('should send challenges that matured while we were offline', async function (this: Mocha.Context) { const handler = await createHandler(); const one = createChallenge('1'); @@ -265,7 +265,7 @@ describe('ChallengeHandler', () => { assert.equal(challengeStatus, 'idle'); }); - it('should trigger onChallengeSolved', async function test() { + it('should trigger onChallengeSolved', async function (this: Mocha.Context) { const onChallengeSolved = sinon.stub(); const handler = await createHandler({ @@ -284,7 +284,7 @@ describe('ChallengeHandler', () => { sinon.assert.calledOnce(onChallengeSolved); }); - it('should trigger onChallengeFailed', async function test() { + it('should trigger onChallengeFailed', async function (this: Mocha.Context) { const onChallengeFailed = sinon.stub(); const handler = await createHandler({ diff --git a/ts/test-both/util/asyncIterables_test.ts b/ts/test-both/util/asyncIterables_test.ts index e650e4cd3a8a..5ccd9d3e2c54 100644 --- a/ts/test-both/util/asyncIterables_test.ts +++ b/ts/test-both/util/asyncIterables_test.ts @@ -57,7 +57,7 @@ describe('async iterable utilities', () => { }); it('resolves to an array when wrapping an asynchronous iterable', async () => { - const iterable = (async function* test() { + const iterable = (async function* () { yield 1; yield 2; yield 3; diff --git a/ts/test-both/util/iterables_test.ts b/ts/test-both/util/iterables_test.ts index 755d62fd8789..a1824fcd71f9 100644 --- a/ts/test-both/util/iterables_test.ts +++ b/ts/test-both/util/iterables_test.ts @@ -59,7 +59,7 @@ describe('iterable utilities', () => { ); assert.isTrue( isIterable( - (function* generators() { + (function* () { yield 123; })() ) diff --git a/ts/test-both/util/startTimeTravelDetector_test.ts b/ts/test-both/util/startTimeTravelDetector_test.ts index 6b720a0cb7df..346cb229b6a6 100644 --- a/ts/test-both/util/startTimeTravelDetector_test.ts +++ b/ts/test-both/util/startTimeTravelDetector_test.ts @@ -16,7 +16,7 @@ describe('startTimeTravelDetector', () => { sandbox.restore(); }); - it('calls the callback when the time between checks is more than 2 seconds', async function test() { + it('calls the callback when the time between checks is more than 2 seconds', async () => { const callback = sandbox.fake(); startTimeTravelDetector(callback); diff --git a/ts/test-both/util/version_test.ts b/ts/test-both/util/version_test.ts index 7d0bd1143520..fa7c115c8be1 100644 --- a/ts/test-both/util/version_test.ts +++ b/ts/test-both/util/version_test.ts @@ -74,17 +74,16 @@ describe('version utilities', () => { }); describe('generateAlphaVersion', () => { - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { // This isn't a hook. - // eslint-disable-next-line react-hooks/rules-of-hooks this.clock = useFakeTimers(); }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.clock.restore(); }); - it('uses the current date and provided shortSha', function test() { + it('uses the current date and provided shortSha', function (this: Mocha.Context) { this.clock.setSystemTime(new Date('2021-07-23T01:22:55.692Z').getTime()); const currentVersion = '5.12.0-beta.1'; @@ -96,7 +95,7 @@ describe('version utilities', () => { assert.strictEqual(expected, actual); }); - it('same production version is semver.gt', function test() { + it('same production version is semver.gt', function (this: Mocha.Context) { const currentVersion = '5.12.0-beta.1'; const shortSha = '07f0efc45'; @@ -106,7 +105,7 @@ describe('version utilities', () => { assert.isTrue(semver.gt('5.12.0', actual)); }); - it('same beta version is semver.gt', function test() { + it('same beta version is semver.gt', function (this: Mocha.Context) { const currentVersion = '5.12.0-beta.1'; const shortSha = '07f0efc45'; @@ -116,7 +115,7 @@ describe('version utilities', () => { assert.isTrue(semver.gt(currentVersion, actual)); }); - it('build earlier same day is semver.lt', function test() { + it('build earlier same day is semver.lt', function (this: Mocha.Context) { const currentVersion = '5.12.0-beta.1'; const shortSha = '07f0efc45'; @@ -129,7 +128,7 @@ describe('version utilities', () => { assert.isTrue(semver.lt(actualEarlier, actualLater)); }); - it('build previous day is semver.lt', function test() { + it('build previous day is semver.lt', function (this: Mocha.Context) { const currentVersion = '5.12.0-beta.1'; const shortSha = '07f0efc45'; diff --git a/ts/test-electron/WebsocketResources_test.ts b/ts/test-electron/WebsocketResources_test.ts index fbc54cdf67a4..cb900e205a71 100644 --- a/ts/test-electron/WebsocketResources_test.ts +++ b/ts/test-electron/WebsocketResources_test.ts @@ -29,7 +29,7 @@ describe('WebSocket-Resource', () => { const NOW = Date.now(); - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.sandbox = sinon.createSandbox(); this.clock = this.sandbox.useFakeTimers({ now: NOW, @@ -42,7 +42,7 @@ describe('WebSocket-Resource', () => { .callsFake(clearTimeout); }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.sandbox.restore(); }); @@ -145,7 +145,7 @@ describe('WebSocket-Resource', () => { resource.close(); }); - it('force closes the connection', function test(done) { + it('force closes the connection', function (this: Mocha.Context, done) { const socket = new FakeSocket(); const resource = new WebSocketResource(socket as WebSocket, { @@ -161,7 +161,7 @@ describe('WebSocket-Resource', () => { }); describe('with a keepalive config', () => { - it('sends keepalives once a minute', function test(done) { + it('sends keepalives once a minute', function (this: Mocha.Context, done) { const socket = new FakeSocket(); sinon.stub(socket, 'sendBytes').callsFake(data => { @@ -180,7 +180,7 @@ describe('WebSocket-Resource', () => { this.clock.next(); }); - it('optionally disconnects if no response', function thisNeeded1(done) { + it('optionally disconnects if no response', function (this: Mocha.Context, done) { const socket = new FakeSocket(); sinon.stub(socket, 'close').callsFake(() => done()); @@ -197,7 +197,7 @@ describe('WebSocket-Resource', () => { this.clock.next(); }); - it('optionally disconnects if suspended', function thisNeeded1(done) { + it('optionally disconnects if suspended', function (this: Mocha.Context, done) { const socket = new FakeSocket(); sinon.stub(socket, 'close').callsFake(() => done()); @@ -212,7 +212,7 @@ describe('WebSocket-Resource', () => { this.clock.next(); }); - it('allows resetting the keepalive timer', function thisNeeded2(done) { + it('allows resetting the keepalive timer', function (this: Mocha.Context, done) { const startTime = Date.now(); const socket = new FakeSocket(); diff --git a/ts/test-electron/linkPreviews/linkPreviewFetch_test.ts b/ts/test-electron/linkPreviews/linkPreviewFetch_test.ts index a11e1e0beb53..462c1827c9ba 100644 --- a/ts/test-electron/linkPreviews/linkPreviewFetch_test.ts +++ b/ts/test-electron/linkPreviews/linkPreviewFetch_test.ts @@ -841,12 +841,12 @@ describe('link preview fetching', () => { sinon.assert.notCalled(shouldNeverBeCalled); }); - it('stops reading bodies after 1000 kilobytes', async function test() { + it('stops reading bodies after 1000 kilobytes', async () => { const shouldNeverBeCalled = sinon.stub(); const fakeFetch = stub().resolves( makeResponse({ - body: (async function* body() { + body: (async function* () { yield new TextEncoder().encode( 'foo bar' ); diff --git a/ts/test-electron/models/messages_test.ts b/ts/test-electron/models/messages_test.ts index 16e990152c0d..d62fe6578851 100644 --- a/ts/test-electron/models/messages_test.ts +++ b/ts/test-electron/models/messages_test.ts @@ -91,11 +91,11 @@ describe('Message', () => { ); }); - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.sandbox = sinon.createSandbox(); }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.sandbox.restore(); }); @@ -103,7 +103,7 @@ describe('Message', () => { describe('send', () => { let oldMessageSender: undefined | MessageSender; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { oldMessageSender = window.textsecure.messaging; window.textsecure.messaging = @@ -124,7 +124,7 @@ describe('Message', () => { } }); - it('updates `sendStateByConversationId`', async function test() { + it('updates `sendStateByConversationId`', async function (this: Mocha.Context) { this.sandbox.useFakeTimers(1234); const ourConversationId = @@ -681,7 +681,7 @@ describe('Message', () => { ); }); - it("shows a notification's emoji on non-Linux", async function test() { + it("shows a notification's emoji on non-Linux", async function (this: Mocha.Context) { this.sandbox.replace(window.Signal, 'OS', { ...window.Signal.OS, isLinux() { @@ -710,7 +710,7 @@ describe('Message', () => { ); }); - it('hides emoji on Linux', async function test() { + it('hides emoji on Linux', async function (this: Mocha.Context) { this.sandbox.replace(window.Signal, 'OS', { ...window.Signal.OS, isLinux() { diff --git a/ts/test-electron/quill/emoji/completion_test.tsx b/ts/test-electron/quill/emoji/completion_test.tsx index 58d4557d2e9d..72a6f9cb6dbe 100644 --- a/ts/test-electron/quill/emoji/completion_test.tsx +++ b/ts/test-electron/quill/emoji/completion_test.tsx @@ -12,7 +12,7 @@ describe('emojiCompletion', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let mockQuill: any; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { mockQuill = { getLeaf: sinon.stub(), getSelection: sinon.stub(), @@ -56,7 +56,7 @@ describe('emojiCompletion', () => { void >; - beforeEach(function beforeEach() { + beforeEach(() => { // eslint-disable-next-line @typescript-eslint/no-explicit-any emojiCompletion.results = [{ short_name: 'joy' } as any]; emojiCompletion.index = 5; @@ -65,12 +65,12 @@ describe('emojiCompletion', () => { .callThrough(); }); - afterEach(function afterEach() { + afterEach(() => { insertEmojiStub.restore(); }); describe('given an emoji is not starting (no colon)', () => { - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index: 3, length: 0, @@ -90,7 +90,7 @@ describe('emojiCompletion', () => { }); describe('given a colon in a string (but not an emoji)', () => { - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index: 5, length: 0, @@ -110,7 +110,7 @@ describe('emojiCompletion', () => { }); describe('given an emoji is starting but does not have 2 characters', () => { - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index: 2, length: 0, @@ -130,7 +130,7 @@ describe('emojiCompletion', () => { }); describe('given an emoji is starting but does not match a short name', () => { - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index: 4, length: 0, @@ -150,7 +150,7 @@ describe('emojiCompletion', () => { }); describe('given an emoji is starting and matches short names', () => { - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index: 4, length: 0, @@ -171,7 +171,7 @@ describe('emojiCompletion', () => { }); describe('given an emoji was just completed', () => { - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index: 7, length: 0, @@ -181,7 +181,7 @@ describe('emojiCompletion', () => { describe('and given it matches a short name', () => { const text = ':smile:'; - beforeEach(function beforeEach() { + beforeEach(() => { const blot = { text, }; @@ -206,7 +206,7 @@ describe('emojiCompletion', () => { describe('and given it matches a short name inside a larger string', () => { const text = 'have a :smile: nice day'; - beforeEach(function beforeEach() { + beforeEach(() => { const blot = { text, }; @@ -242,7 +242,7 @@ describe('emojiCompletion', () => { describe('and given it does not match a short name', () => { const text = ':smyle:'; - beforeEach(function beforeEach() { + beforeEach(() => { const blot = { text, }; @@ -262,7 +262,7 @@ describe('emojiCompletion', () => { const invalidEmoji = ':smyle:'; const middleCursorIndex = validEmoji.length - 3; - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index: middleCursorIndex, length: 0, @@ -270,7 +270,7 @@ describe('emojiCompletion', () => { }); describe('and given it matches a short name', () => { - beforeEach(function beforeEach() { + beforeEach(() => { const blot = { text: validEmoji, }; @@ -293,7 +293,7 @@ describe('emojiCompletion', () => { }); describe('and given it does not match a short name', () => { - beforeEach(function beforeEach() { + beforeEach(() => { const blot = { text: invalidEmoji, }; @@ -309,7 +309,7 @@ describe('emojiCompletion', () => { }); describe('given a completeable emoji and colon was just pressed', () => { - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index: 6, length: 0, @@ -319,7 +319,7 @@ describe('emojiCompletion', () => { describe('and given it matches a short name', () => { const text = ':smile'; - beforeEach(function beforeEach() { + beforeEach(() => { const blot = { text, }; @@ -349,7 +349,7 @@ describe('emojiCompletion', () => { void >; - beforeEach(function beforeEach() { + beforeEach(() => { emojiCompletion.results = [ // eslint-disable-next-line @typescript-eslint/no-explicit-any { short_name: 'smile' } as any, @@ -364,7 +364,7 @@ describe('emojiCompletion', () => { const text = ':smi'; const index = text.length; - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index, length: 0, @@ -391,7 +391,7 @@ describe('emojiCompletion', () => { const text = 'smi'; const index = text.length; - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection.returns({ index, length: 0, diff --git a/ts/test-electron/quill/mentions/completion_test.tsx b/ts/test-electron/quill/mentions/completion_test.tsx index 551eb40d6094..94097ee0fef2 100644 --- a/ts/test-electron/quill/mentions/completion_test.tsx +++ b/ts/test-electron/quill/mentions/completion_test.tsx @@ -74,7 +74,7 @@ describe('MentionCompletion', () => { }; let mentionCompletion: MentionCompletion; - beforeEach(function beforeEach() { + beforeEach(() => { const memberRepositoryRef: MutableRefObject = { current: new MemberRepository(conversations), }; @@ -234,7 +234,7 @@ describe('MentionCompletion', () => { const text = '@Sh'; const index = text.length; - beforeEach(function beforeEach() { + beforeEach(() => { mockQuill.getSelection?.returns({ index }); const blot = { diff --git a/ts/test-electron/scrollUtil_test.ts b/ts/test-electron/scrollUtil_test.ts index fdb1eade7482..6664c76ddde3 100644 --- a/ts/test-electron/scrollUtil_test.ts +++ b/ts/test-electron/scrollUtil_test.ts @@ -16,7 +16,7 @@ describe('scroll utilities', () => { // These tests to be flaky on Windows CI, sometimes timing out. That doesn't really // make sense because the test is synchronous, but this quick-and-dirty fix is // probably better than a full investigation. - before(function thisNeeded() { + before(function (this: Mocha.Context) { if (process.platform === 'win32') { this.skip(); } diff --git a/ts/test-electron/services/ActiveWindowService_test.ts b/ts/test-electron/services/ActiveWindowService_test.ts index 8d89e15e21b7..15187f81885c 100644 --- a/ts/test-electron/services/ActiveWindowService_test.ts +++ b/ts/test-electron/services/ActiveWindowService_test.ts @@ -10,11 +10,11 @@ import { getActiveWindowService } from '../../services/ActiveWindowService'; describe('ActiveWindowService', () => { const fakeIpcEvent = {}; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.clock = sinon.useFakeTimers({ now: 1000 }); }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.clock.restore(); }); @@ -40,7 +40,7 @@ describe('ActiveWindowService', () => { assert.isTrue(service.isActive()); }); - it('becomes inactive after 15 seconds without interaction', function test() { + it('becomes inactive after 15 seconds without interaction', function (this: Mocha.Context) { const fakeIpc = new EventEmitter(); const service = getActiveWindowService(createFakeDocument(), fakeIpc); @@ -58,7 +58,7 @@ describe('ActiveWindowService', () => { ['click', 'keydown', 'mousedown', 'mousemove', 'touchstart', 'wheel'].forEach( (eventName: string) => { - it(`is inactive even in the face of ${eventName} events if unfocused`, function test() { + it(`is inactive even in the face of ${eventName} events if unfocused`, function (this: Mocha.Context) { const fakeDocument = createFakeDocument(); const fakeIpc = new EventEmitter(); const service = getActiveWindowService(fakeDocument, fakeIpc); @@ -69,7 +69,7 @@ describe('ActiveWindowService', () => { assert.isFalse(service.isActive()); }); - it(`stays active if focused and receiving ${eventName} events`, function test() { + it(`stays active if focused and receiving ${eventName} events`, function (this: Mocha.Context) { const fakeDocument = createFakeDocument(); const fakeIpc = new EventEmitter(); const service = getActiveWindowService(fakeDocument, fakeIpc); @@ -102,7 +102,7 @@ describe('ActiveWindowService', () => { sinon.assert.calledOnce(callback); }); - it('calls callbacks when receiving a click event after being focused', function test() { + it('calls callbacks when receiving a click event after being focused', function (this: Mocha.Context) { const fakeDocument = createFakeDocument(); const fakeIpc = new EventEmitter(); const service = getActiveWindowService(fakeDocument, fakeIpc); @@ -119,7 +119,7 @@ describe('ActiveWindowService', () => { sinon.assert.calledOnce(callback); }); - it('only calls callbacks every 5 seconds; it is throttled', function test() { + it('only calls callbacks every 5 seconds; it is throttled', function (this: Mocha.Context) { const fakeIpc = new EventEmitter(); const service = getActiveWindowService(createFakeDocument(), fakeIpc); diff --git a/ts/test-electron/state/ducks/calling_test.ts b/ts/test-electron/state/ducks/calling_test.ts index e686d0726ab2..788d5d485f66 100644 --- a/ts/test-electron/state/ducks/calling_test.ts +++ b/ts/test-electron/state/ducks/calling_test.ts @@ -186,7 +186,7 @@ describe('calling duck', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let oldEvents: any; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.sandbox = sinon.createSandbox(); oldEvents = window.Events; @@ -196,7 +196,7 @@ describe('calling duck', () => { } as any; }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.sandbox.restore(); window.Events = oldEvents; @@ -204,7 +204,7 @@ describe('calling duck', () => { describe('actions', () => { describe('getPresentingSources', () => { - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.callingServiceGetPresentingSources = this.sandbox .stub(callingService, 'getPresentingSources') .resolves([ @@ -216,7 +216,7 @@ describe('calling duck', () => { ]); }); - it('retrieves sources from the calling service', async function test() { + it('retrieves sources from the calling service', async function (this: Mocha.Context) { const { getPresentingSources } = actions; const dispatch = sinon.spy(); await getPresentingSources()(dispatch, getEmptyRootState, null); @@ -224,7 +224,7 @@ describe('calling duck', () => { sinon.assert.calledOnce(this.callingServiceGetPresentingSources); }); - it('dispatches SET_PRESENTING_SOURCES', async function test() { + it('dispatches SET_PRESENTING_SOURCES', async () => { const { getPresentingSources } = actions; const dispatch = sinon.spy(); await getPresentingSources()(dispatch, getEmptyRootState, null); @@ -272,14 +272,14 @@ describe('calling duck', () => { }); describe('setPresenting', () => { - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.callingServiceSetPresenting = this.sandbox.stub( callingService, 'setPresenting' ); }); - it('calls setPresenting on the calling service', async function test() { + it('calls setPresenting on the calling service', async function (this: Mocha.Context) { const { setPresenting } = actions; const dispatch = sinon.spy(); const presentedSource = { @@ -386,7 +386,7 @@ describe('calling duck', () => { describe('acceptCall', () => { const { acceptCall } = actions; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.callingServiceAccept = this.sandbox .stub(callingService, 'acceptDirectCall') .resolves(); @@ -433,7 +433,7 @@ describe('calling duck', () => { }); }); - it('asks the calling service to accept the call', async function test() { + it('asks the calling service to accept the call', async function (this: Mocha.Context) { const dispatch = sinon.spy(); await acceptCall({ @@ -525,7 +525,7 @@ describe('calling duck', () => { }); }); - it('asks the calling service to join the call', async function test() { + it('asks the calling service to join the call', async function (this: Mocha.Context) { const dispatch = sinon.spy(); await acceptCall({ @@ -585,14 +585,14 @@ describe('calling duck', () => { describe('cancelCall', () => { const { cancelCall } = actions; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.callingServiceStopCallingLobby = this.sandbox.stub( callingService, 'stopCallingLobby' ); }); - it('stops the calling lobby for that conversation', function test() { + it('stops the calling lobby for that conversation', function (this: Mocha.Context) { cancelCall({ conversationId: '123' }); sinon.assert.calledOnce(this.callingServiceStopCallingLobby); @@ -677,7 +677,7 @@ describe('calling duck', () => { let declineDirectCall: sinon.SinonStub; let declineGroupCall: sinon.SinonStub; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { declineDirectCall = this.sandbox.stub( callingService, 'declineDirectCall' @@ -1269,7 +1269,7 @@ describe('calling duck', () => { describe('peekNotConnectedGroupCall', () => { const { peekNotConnectedGroupCall } = actions; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.callingServicePeekGroupCall = this.sandbox.stub( callingService, 'peekGroupCall' @@ -1439,7 +1439,7 @@ describe('calling duck', () => { describe('setLocalAudio', () => { const { setLocalAudio } = actions; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.callingServiceSetOutgoingAudio = this.sandbox.stub( callingService, 'setOutgoingAudio' @@ -1465,7 +1465,7 @@ describe('calling duck', () => { }); }); - it('updates the outgoing audio for the active call', function test() { + it('updates the outgoing audio for the active call', function (this: Mocha.Context) { const dispatch = sinon.spy(); setLocalAudio({ enabled: false })( @@ -1543,7 +1543,7 @@ describe('calling duck', () => { let rootState: RootStateType; let startCallingLobbyStub: sinon.SinonStub; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { startCallingLobbyStub = this.sandbox .stub(callingService, 'startCallingLobby') .resolves(); @@ -1913,7 +1913,7 @@ describe('calling duck', () => { describe('startCall', () => { const { startCall } = actions; - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.callingStartOutgoingDirectCall = this.sandbox.stub( callingService, 'startOutgoingDirectCall' @@ -1923,7 +1923,7 @@ describe('calling duck', () => { .resolves(); }); - it('asks the calling service to start an outgoing direct call', async function test() { + it('asks the calling service to start an outgoing direct call', async function (this: Mocha.Context) { const dispatch = sinon.spy(); await startCall({ callMode: CallMode.Direct, @@ -1943,7 +1943,7 @@ describe('calling duck', () => { sinon.assert.notCalled(this.callingJoinGroupCall); }); - it('asks the calling service to join a group call', async function test() { + it('asks the calling service to join a group call', async function (this: Mocha.Context) { const dispatch = sinon.spy(); await startCall({ callMode: CallMode.Group, diff --git a/ts/test-electron/state/ducks/stories_test.ts b/ts/test-electron/state/ducks/stories_test.ts index 916382474ac5..1ada933224e2 100644 --- a/ts/test-electron/state/ducks/stories_test.ts +++ b/ts/test-electron/state/ducks/stories_test.ts @@ -857,7 +857,7 @@ describe('both/state/ducks/stories', () => { describe('queueStoryDownload', () => { const { queueStoryDownload } = actions; - it('no attachment, no dispatch', async function test() { + it('no attachment, no dispatch', async () => { const storyId = generateUuid(); const messageAttributes = getStoryMessage(storyId); @@ -873,7 +873,7 @@ describe('both/state/ducks/stories', () => { sinon.assert.notCalled(dispatch); }); - it('downloading, no dispatch', async function test() { + it('downloading, no dispatch', async () => { const storyId = generateUuid(); const messageAttributes = { ...getStoryMessage(storyId), @@ -899,7 +899,7 @@ describe('both/state/ducks/stories', () => { sinon.assert.notCalled(dispatch); }); - it('downloaded, no dispatch', async function test() { + it('downloaded, no dispatch', async () => { const storyId = generateUuid(); const messageAttributes = { ...getStoryMessage(storyId), @@ -925,7 +925,7 @@ describe('both/state/ducks/stories', () => { sinon.assert.notCalled(dispatch); }); - it('not downloaded, queued for download', async function test() { + it('not downloaded, queued for download', async () => { const storyId = generateUuid(); const messageAttributes = { ...getStoryMessage(storyId), @@ -974,7 +974,7 @@ describe('both/state/ducks/stories', () => { }); }); - it('preview not downloaded, queued for download', async function test() { + it('preview not downloaded, queued for download', async () => { const storyId = generateUuid(); const preview = { url: 'https://signal.org', diff --git a/ts/test-electron/textsecure/generate_keys_test.ts b/ts/test-electron/textsecure/generate_keys_test.ts index 20698e9e9c87..9238a1e3e6d2 100644 --- a/ts/test-electron/textsecure/generate_keys_test.ts +++ b/ts/test-electron/textsecure/generate_keys_test.ts @@ -17,7 +17,7 @@ const assertEqualBuffers = (a: Uint8Array, b: Uint8Array) => { assert.isTrue(constantTimeEqual(a, b)); }; -describe('Key generation', function thisNeeded() { +describe('Key generation', function (this: Mocha.Suite) { const count = 10; const ourServiceId = normalizeAci( 'aaaaaaaa-bbbb-4ccc-9ddd-eeeeeeeeeeee', diff --git a/ts/test-mock/messaging/edit_test.ts b/ts/test-mock/messaging/edit_test.ts index 2165725e715c..620d40033b41 100644 --- a/ts/test-mock/messaging/edit_test.ts +++ b/ts/test-mock/messaging/edit_test.ts @@ -54,7 +54,7 @@ function createEditedMessage( }; } -describe('editing', function needsName() { +describe('editing', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -67,7 +67,7 @@ describe('editing', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/messaging/readSync_test.ts b/ts/test-mock/messaging/readSync_test.ts index db96d98cac10..4d19e1281536 100644 --- a/ts/test-mock/messaging/readSync_test.ts +++ b/ts/test-mock/messaging/readSync_test.ts @@ -11,7 +11,7 @@ import { Bootstrap } from '../bootstrap'; export const debug = createDebug('mock:test:readSync'); -describe('readSync', function needsName() { +describe('readSync', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -24,7 +24,7 @@ describe('readSync', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/messaging/sender_key_test.ts b/ts/test-mock/messaging/sender_key_test.ts index 14e88108570b..d837c6f06e08 100644 --- a/ts/test-mock/messaging/sender_key_test.ts +++ b/ts/test-mock/messaging/sender_key_test.ts @@ -11,7 +11,7 @@ import { Bootstrap } from '../bootstrap'; export const debug = createDebug('mock:test:senderKey'); -describe('senderKey', function needsName() { +describe('senderKey', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -50,7 +50,7 @@ describe('senderKey', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/messaging/stories_test.ts b/ts/test-mock/messaging/stories_test.ts index 926b0b4166b7..660462606cd2 100644 --- a/ts/test-mock/messaging/stories_test.ts +++ b/ts/test-mock/messaging/stories_test.ts @@ -20,7 +20,7 @@ const IdentifierType = Proto.ManifestRecord.Identifier.Type; const DISTRIBUTION1 = generateStoryDistributionId(); const DISTRIBUTION2 = generateStoryDistributionId(); -describe('story/messaging', function unknownContacts() { +describe('story/messaging', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -114,7 +114,7 @@ describe('story/messaging', function unknownContacts() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/messaging/unknown_contact_test.ts b/ts/test-mock/messaging/unknown_contact_test.ts index 38e453ebdab0..b47c019135cf 100644 --- a/ts/test-mock/messaging/unknown_contact_test.ts +++ b/ts/test-mock/messaging/unknown_contact_test.ts @@ -12,7 +12,7 @@ import { Bootstrap } from '../bootstrap'; export const debug = createDebug('mock:test:edit'); -describe('unknown contacts', function unknownContacts() { +describe('unknown contacts', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -36,7 +36,7 @@ describe('unknown contacts', function unknownContacts() { await unknownContact.addSingleUseKey(desktop, ourKey); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/pnp/accept_gv2_invite_test.ts b/ts/test-mock/pnp/accept_gv2_invite_test.ts index e10817a8ec0c..4f07575575c5 100644 --- a/ts/test-mock/pnp/accept_gv2_invite_test.ts +++ b/ts/test-mock/pnp/accept_gv2_invite_test.ts @@ -12,7 +12,7 @@ import type { App } from '../bootstrap'; export const debug = createDebug('mock:test:gv2'); -describe('pnp/accept gv2 invite', function needsName() { +describe('pnp/accept gv2 invite', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -56,7 +56,7 @@ describe('pnp/accept gv2 invite', function needsName() { await leftPane.locator(`[data-testid="${group.id}"]`).click(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/pnp/change_number_test.ts b/ts/test-mock/pnp/change_number_test.ts index 1871c6e5ca7b..8c8c8bdf46b1 100644 --- a/ts/test-mock/pnp/change_number_test.ts +++ b/ts/test-mock/pnp/change_number_test.ts @@ -10,7 +10,7 @@ import type { App } from '../bootstrap'; export const debug = createDebug('mock:test:change-number'); -describe('pnp/change number', function needsName() { +describe('pnp/change number', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -23,7 +23,7 @@ describe('pnp/change number', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/pnp/merge_test.ts b/ts/test-mock/pnp/merge_test.ts index 4f87c70dd215..a73c63047747 100644 --- a/ts/test-mock/pnp/merge_test.ts +++ b/ts/test-mock/pnp/merge_test.ts @@ -18,7 +18,7 @@ export const debug = createDebug('mock:test:merge'); const IdentifierType = Proto.ManifestRecord.Identifier.Type; -describe('pnp/merge', function needsName() { +describe('pnp/merge', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -93,7 +93,7 @@ describe('pnp/merge', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/pnp/pni_change_test.ts b/ts/test-mock/pnp/pni_change_test.ts index 2fd6e5095d4c..d6718411334e 100644 --- a/ts/test-mock/pnp/pni_change_test.ts +++ b/ts/test-mock/pnp/pni_change_test.ts @@ -13,7 +13,7 @@ import type { App } from '../bootstrap'; export const debug = createDebug('mock:test:pni-change'); -describe('pnp/PNI Change', function needsName() { +describe('pnp/PNI Change', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -62,7 +62,7 @@ describe('pnp/PNI Change', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/pnp/pni_signature_test.ts b/ts/test-mock/pnp/pni_signature_test.ts index 22a22d3c1949..c91d9458d115 100644 --- a/ts/test-mock/pnp/pni_signature_test.ts +++ b/ts/test-mock/pnp/pni_signature_test.ts @@ -23,7 +23,7 @@ export const debug = createDebug('mock:test:pni-signature'); const IdentifierType = Proto.ManifestRecord.Identifier.Type; -describe('pnp/PNI Signature', function needsName() { +describe('pnp/PNI Signature', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -88,7 +88,7 @@ describe('pnp/PNI Signature', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/pnp/pni_unlink_test.ts b/ts/test-mock/pnp/pni_unlink_test.ts index 1fcebe0ec895..dd92249dd616 100644 --- a/ts/test-mock/pnp/pni_unlink_test.ts +++ b/ts/test-mock/pnp/pni_unlink_test.ts @@ -18,7 +18,7 @@ import type { App } from '../bootstrap'; export const debug = createDebug('mock:test:pni-unlink'); -describe('pnp/PNI DecryptionError unlink', function needsName() { +describe('pnp/PNI DecryptionError unlink', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -34,7 +34,7 @@ describe('pnp/PNI DecryptionError unlink', function needsName() { await bootstrap.linkAndClose(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (app) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); diff --git a/ts/test-mock/pnp/send_gv2_invite_test.ts b/ts/test-mock/pnp/send_gv2_invite_test.ts index 7925a2adb710..01cca4a5fe3d 100644 --- a/ts/test-mock/pnp/send_gv2_invite_test.ts +++ b/ts/test-mock/pnp/send_gv2_invite_test.ts @@ -16,7 +16,7 @@ const IdentifierType = Proto.ManifestRecord.Identifier.Type; export const debug = createDebug('mock:test:gv2'); -describe('pnp/send gv2 invite', function needsName() { +describe('pnp/send gv2 invite', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -85,7 +85,7 @@ describe('pnp/send gv2 invite', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/pnp/username_test.ts b/ts/test-mock/pnp/username_test.ts index 31031efe7211..e9707c105d0f 100644 --- a/ts/test-mock/pnp/username_test.ts +++ b/ts/test-mock/pnp/username_test.ts @@ -23,7 +23,7 @@ const USERNAME = 'signalapp.55'; const NICKNAME = 'signalapp'; const CARL_USERNAME = 'carl.84'; -describe('pnp/username', function needsName() { +describe('pnp/username', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -75,7 +75,7 @@ describe('pnp/username', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/rate-limit/story_test.ts b/ts/test-mock/rate-limit/story_test.ts index e00a31c2882c..456da0f12fda 100644 --- a/ts/test-mock/rate-limit/story_test.ts +++ b/ts/test-mock/rate-limit/story_test.ts @@ -15,7 +15,7 @@ export const debug = createDebug('mock:test:rate-limit'); const IdentifierType = Proto.ManifestRecord.Identifier.Type; -describe('story/no-sender-key', function needsName() { +describe('story/no-sender-key', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -57,7 +57,7 @@ describe('story/no-sender-key', function needsName() { app = await bootstrap.link(); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/rate-limit/viewed_test.ts b/ts/test-mock/rate-limit/viewed_test.ts index f41efe0ec287..4db8b0d562ca 100644 --- a/ts/test-mock/rate-limit/viewed_test.ts +++ b/ts/test-mock/rate-limit/viewed_test.ts @@ -13,7 +13,7 @@ import { toUntaggedPni } from '../../types/ServiceId'; export const debug = createDebug('mock:test:challenge:receipts'); -describe('challenge/receipts', function challengeReceiptsTest() { +describe('challenge/receipts', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -65,7 +65,7 @@ describe('challenge/receipts', function challengeReceiptsTest() { await phone.setStorageState(state); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { await bootstrap.maybeSaveLogs(this.currentTest, app); await app.close(); await bootstrap.teardown(); diff --git a/ts/test-mock/storage/archive_test.ts b/ts/test-mock/storage/archive_test.ts index 7947eeb08109..f2e0631c3a7c 100644 --- a/ts/test-mock/storage/archive_test.ts +++ b/ts/test-mock/storage/archive_test.ts @@ -7,7 +7,7 @@ import * as durations from '../../util/durations'; import type { App, Bootstrap } from './fixtures'; import { initStorage, debug } from './fixtures'; -describe('storage service', function needsName() { +describe('storage service', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -18,7 +18,7 @@ describe('storage service', function needsName() { ({ bootstrap, app } = await initStorage()); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/storage/drop_test.ts b/ts/test-mock/storage/drop_test.ts index fc98f7deeba2..89836791d756 100644 --- a/ts/test-mock/storage/drop_test.ts +++ b/ts/test-mock/storage/drop_test.ts @@ -10,7 +10,7 @@ import { initStorage, debug } from './fixtures'; const IdentifierType = Proto.ManifestRecord.Identifier.Type; -describe('storage service', function needsName() { +describe('storage service', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -21,7 +21,7 @@ describe('storage service', function needsName() { ({ bootstrap, app } = await initStorage()); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/storage/max_read_keys_test.ts b/ts/test-mock/storage/max_read_keys_test.ts index 8216570d0e0a..a6dbe67bf22c 100644 --- a/ts/test-mock/storage/max_read_keys_test.ts +++ b/ts/test-mock/storage/max_read_keys_test.ts @@ -12,7 +12,7 @@ import { initStorage, debug } from './fixtures'; const IdentifierType = Proto.ManifestRecord.Identifier.Type; -describe('storage service', function needsName() { +describe('storage service', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -23,7 +23,7 @@ describe('storage service', function needsName() { ({ bootstrap, app } = await initStorage()); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/storage/message_request_test.ts b/ts/test-mock/storage/message_request_test.ts index af31ff4e5163..3af91267f4e2 100644 --- a/ts/test-mock/storage/message_request_test.ts +++ b/ts/test-mock/storage/message_request_test.ts @@ -7,7 +7,7 @@ import * as durations from '../../util/durations'; import type { App, Bootstrap } from './fixtures'; import { initStorage, debug } from './fixtures'; -describe('storage service', function needsName() { +describe('storage service', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -18,7 +18,7 @@ describe('storage service', function needsName() { ({ bootstrap, app } = await initStorage()); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/storage/pin_unpin_test.ts b/ts/test-mock/storage/pin_unpin_test.ts index dda379f618f7..02af2c441c7d 100644 --- a/ts/test-mock/storage/pin_unpin_test.ts +++ b/ts/test-mock/storage/pin_unpin_test.ts @@ -10,7 +10,7 @@ import * as durations from '../../util/durations'; import type { App, Bootstrap } from './fixtures'; import { initStorage, debug } from './fixtures'; -describe('storage service', function needsName() { +describe('storage service', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -22,7 +22,7 @@ describe('storage service', function needsName() { ({ bootstrap, app, group } = await initStorage()); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-mock/storage/sticker_test.ts b/ts/test-mock/storage/sticker_test.ts index 75656f288605..34f76a104d4d 100644 --- a/ts/test-mock/storage/sticker_test.ts +++ b/ts/test-mock/storage/sticker_test.ts @@ -62,7 +62,7 @@ function getStickerPackRecordPredicate( }; } -describe('storage service', function needsName() { +describe('storage service', function (this: Mocha.Suite) { this.timeout(durations.MINUTE); this.retries(4); @@ -95,7 +95,7 @@ describe('storage service', function needsName() { ); }); - afterEach(async function after() { + afterEach(async function (this: Mocha.Context) { if (!bootstrap) { return; } diff --git a/ts/test-node/app/SystemTrayService_test.ts b/ts/test-node/app/SystemTrayService_test.ts index 635af93f1e21..c2ced014ee47 100644 --- a/ts/test-node/app/SystemTrayService_test.ts +++ b/ts/test-node/app/SystemTrayService_test.ts @@ -16,7 +16,7 @@ import enMessages from '../../../_locales/en/messages.json'; const i18n = setupI18n('en', enMessages); -describe('SystemTrayService', function thisNeeded() { +describe('SystemTrayService', function (this: Mocha.Suite) { // These tests take more time on CI in some cases, so we increase the timeout. this.timeout(MINUTE); diff --git a/ts/test-node/app/base_config_test.ts b/ts/test-node/app/base_config_test.ts index 6d4d15958f67..43a755f9738f 100644 --- a/ts/test-node/app/base_config_test.ts +++ b/ts/test-node/app/base_config_test.ts @@ -83,7 +83,7 @@ describe('base_config', () => { assert.deepEqual(_getCachedValue(), Object.create(null)); }); - it('handles a file that cannot be opened, if told to', function test() { + it('handles a file that cannot be opened, if told to', function (this: Mocha.Context) { if (process.platform === 'win32') { this.skip(); } @@ -227,7 +227,7 @@ describe('base_config', () => { }); describe('throwOnFilesystemErrors: true', () => { - it("doesn't update the local cache if file removal fails", async function test() { + it("doesn't update the local cache if file removal fails", async function (this: Mocha.Context) { if (process.platform === 'win32') { this.skip(); } @@ -252,7 +252,7 @@ describe('base_config', () => { }); describe('throwOnFilesystemErrors: false', () => { - it('updates the local cache even if file removal fails', async function test() { + it('updates the local cache even if file removal fails', async function (this: Mocha.Context) { if (process.platform === 'win32') { this.skip(); } diff --git a/ts/test-node/logging/uploadDebugLogs_test.ts b/ts/test-node/logging/uploadDebugLogs_test.ts index bd6bc1234a30..f624e3653e33 100644 --- a/ts/test-node/logging/uploadDebugLogs_test.ts +++ b/ts/test-node/logging/uploadDebugLogs_test.ts @@ -15,7 +15,7 @@ import * as logger from '../../logging/log'; const gzip: (_: zlib.InputType) => Promise = util.promisify(zlib.gzip); describe('upload', () => { - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.sandbox = sinon.createSandbox(); this.sandbox.stub(process, 'platform').get(() => 'freebsd'); @@ -35,11 +35,11 @@ describe('upload', () => { this.fakePost.resolves({ statusCode: 204 }); }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.sandbox.restore(); }); - it('makes a request to get the S3 bucket, then uploads it there', async function test() { + it('makes a request to get the S3 bucket, then uploads it there', async function (this: Mocha.Context) { assert.strictEqual( await upload({ content: 'hello world', appVersion: '1.2.3', logger }), 'https://debuglogs.org/abc123.gz' @@ -75,7 +75,7 @@ describe('upload', () => { }); }); - it("rejects if we can't get a token", async function test() { + it("rejects if we can't get a token", async function (this: Mocha.Context) { this.fakeGet.rejects(new Error('HTTP request failure')); let err: unknown; @@ -87,7 +87,7 @@ describe('upload', () => { assert.instanceOf(err, Error); }); - it('rejects with an invalid token body', async function test() { + it('rejects with an invalid token body', async function (this: Mocha.Context) { const bodies = [ null, {}, @@ -114,7 +114,7 @@ describe('upload', () => { } }); - it("rejects if the upload doesn't return a 204", async function test() { + it("rejects if the upload doesn't return a 204", async function (this: Mocha.Context) { this.fakePost.resolves({ statusCode: 400 }); let err: unknown; diff --git a/ts/test-node/util/getUserAgent_test.ts b/ts/test-node/util/getUserAgent_test.ts index 4761d1ae6951..5eedc7a4468f 100644 --- a/ts/test-node/util/getUserAgent_test.ts +++ b/ts/test-node/util/getUserAgent_test.ts @@ -7,15 +7,15 @@ import * as sinon from 'sinon'; import { getUserAgent } from '../../util/getUserAgent'; describe('getUserAgent', () => { - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { this.sandbox = sinon.createSandbox(); }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.sandbox.restore(); }); - it('returns the right User-Agent on Windows', function test() { + it('returns the right User-Agent on Windows', function (this: Mocha.Context) { this.sandbox.stub(process, 'platform').get(() => 'win32'); assert.strictEqual( getUserAgent('1.2.3', '10.0.22000'), @@ -23,7 +23,7 @@ describe('getUserAgent', () => { ); }); - it('returns the right User-Agent on macOS', function test() { + it('returns the right User-Agent on macOS', function (this: Mocha.Context) { this.sandbox.stub(process, 'platform').get(() => 'darwin'); assert.strictEqual( getUserAgent('1.2.3', '21.5.0'), @@ -31,7 +31,7 @@ describe('getUserAgent', () => { ); }); - it('returns the right User-Agent on Linux', function test() { + it('returns the right User-Agent on Linux', function (this: Mocha.Context) { this.sandbox.stub(process, 'platform').get(() => 'linux'); assert.strictEqual( getUserAgent('1.2.3', '20.04'), @@ -39,7 +39,7 @@ describe('getUserAgent', () => { ); }); - it('omits the platform on unsupported platforms', function test() { + it('omits the platform on unsupported platforms', function (this: Mocha.Context) { this.sandbox.stub(process, 'platform').get(() => 'freebsd'); assert.strictEqual(getUserAgent('1.2.3', '13.1'), 'Signal-Desktop/1.2.3'); }); diff --git a/ts/test-node/util/sleep_test.ts b/ts/test-node/util/sleep_test.ts index 5ee1660fbf9a..6165d32b2e91 100644 --- a/ts/test-node/util/sleep_test.ts +++ b/ts/test-node/util/sleep_test.ts @@ -7,17 +7,16 @@ import { useFakeTimers } from 'sinon'; import { sleep } from '../../util/sleep'; describe('sleep', () => { - beforeEach(function beforeEach() { + beforeEach(function (this: Mocha.Context) { // This isn't a hook. - // eslint-disable-next-line react-hooks/rules-of-hooks this.clock = useFakeTimers(); }); - afterEach(function afterEach() { + afterEach(function (this: Mocha.Context) { this.clock.restore(); }); - it('returns a promise that resolves after the specified number of milliseconds', async function test() { + it('returns a promise that resolves after the specified number of milliseconds', async function (this: Mocha.Context) { let isDone = false; void (async () => { diff --git a/ts/test-node/util/windowsZoneIdentifier_test.ts b/ts/test-node/util/windowsZoneIdentifier_test.ts index fdab8071824d..74f70bcffb15 100644 --- a/ts/test-node/util/windowsZoneIdentifier_test.ts +++ b/ts/test-node/util/windowsZoneIdentifier_test.ts @@ -11,25 +11,25 @@ import * as Sinon from 'sinon'; import { writeWindowsZoneIdentifier } from '../../util/windowsZoneIdentifier'; describe('writeWindowsZoneIdentifier', () => { - before(function thisNeeded() { + before(function (this: Mocha.Context) { if (process.platform !== 'win32') { this.skip(); } }); - beforeEach(async function thisNeeded() { + beforeEach(async function (this: Mocha.Context) { this.sandbox = Sinon.createSandbox(); this.tmpdir = await fs.promises.mkdtemp( path.join(os.tmpdir(), 'signal-test-') ); }); - afterEach(async function thisNeeded() { + afterEach(async function (this: Mocha.Context) { this.sandbox.restore(); await fse.remove(this.tmpdir); }); - it('writes zone transfer ID 3 (internet) to the Zone.Identifier file', async function thisNeeded() { + it('writes zone transfer ID 3 (internet) to the Zone.Identifier file', async function (this: Mocha.Context) { const file = path.join(this.tmpdir, 'file.txt'); await fse.outputFile(file, 'hello'); @@ -41,7 +41,7 @@ describe('writeWindowsZoneIdentifier', () => { ); }); - it('fails if there is an existing Zone.Identifier file', async function thisNeeded() { + it('fails if there is an existing Zone.Identifier file', async function (this: Mocha.Context) { const file = path.join(this.tmpdir, 'file.txt'); await fse.outputFile(file, 'hello'); await fs.promises.writeFile(`${file}:Zone.Identifier`, '# already here'); @@ -49,13 +49,13 @@ describe('writeWindowsZoneIdentifier', () => { await assert.isRejected(writeWindowsZoneIdentifier(file)); }); - it('fails if the original file does not exist', async function thisNeeded() { + it('fails if the original file does not exist', async function (this: Mocha.Context) { const file = path.join(this.tmpdir, 'file-never-created.txt'); await assert.isRejected(writeWindowsZoneIdentifier(file)); }); - it('fails if not on Windows', async function thisNeeded() { + it('fails if not on Windows', async function (this: Mocha.Context) { this.sandbox.stub(process, 'platform').get(() => 'darwin'); const file = path.join(this.tmpdir, 'file.txt'); diff --git a/ts/textsecure/cds/CDSSocketBase.ts b/ts/textsecure/cds/CDSSocketBase.ts index ccb730af1df1..69f4160d910c 100644 --- a/ts/textsecure/cds/CDSSocketBase.ts +++ b/ts/textsecure/cds/CDSSocketBase.ts @@ -12,7 +12,7 @@ import { strictAssert } from '../../util/assert'; import { isUntaggedPniString, toTaggedPni } from '../../types/ServiceId'; import { isAciString } from '../../util/isAciString'; import * as Bytes from '../../Bytes'; -import { UUID_BYTE_SIZE } from '../../Crypto'; +import { UUID_BYTE_SIZE } from '../../types/Crypto'; import { uuidToBytes, bytesToUuid } from '../../util/uuidToBytes'; import { SignalService as Proto } from '../../protobuf'; import type { diff --git a/ts/types/Crypto.ts b/ts/types/Crypto.ts index 5bd856c3b02e..c3b847b7c97b 100644 --- a/ts/types/Crypto.ts +++ b/ts/types/Crypto.ts @@ -11,3 +11,5 @@ export enum CipherType { AES256CTR = 'aes-256-ctr', AES256GCM = 'aes-256-gcm', } + +export const UUID_BYTE_SIZE = 16; diff --git a/ts/types/ForwardDraft.ts b/ts/types/ForwardDraft.ts new file mode 100644 index 000000000000..91f98520beb2 --- /dev/null +++ b/ts/types/ForwardDraft.ts @@ -0,0 +1,81 @@ +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { orderBy } from 'lodash'; +import type { MessageAttributesType } from '../model-types'; +import { + isVoiceMessage, + type AttachmentType, + isDownloaded, +} from './Attachment'; +import type { HydratedBodyRangesType } from './BodyRange'; +import type { LinkPreviewType } from './message/LinkPreviews'; + +export type MessageForwardDraft = Readonly<{ + attachments?: ReadonlyArray; + bodyRanges?: HydratedBodyRangesType; + hasContact: boolean; + isSticker: boolean; + messageBody?: string; + originalMessageId: string; + previews: ReadonlyArray; +}>; + +export type ForwardMessageData = Readonly<{ + originalMessage: MessageAttributesType; + draft: MessageForwardDraft; +}>; + +export function isDraftEditable(draft: MessageForwardDraft): boolean { + if (draft.isSticker) { + return false; + } + if (draft.hasContact) { + return false; + } + const hasVoiceMessage = draft.attachments?.some(isVoiceMessage) ?? false; + if (hasVoiceMessage) { + return false; + } + return true; +} + +function isDraftEmpty(draft: MessageForwardDraft) { + const { messageBody, attachments, isSticker, hasContact } = draft; + if (isSticker || hasContact) { + return false; + } + if (attachments != null && attachments.length > 0) { + return false; + } + if (messageBody != null && messageBody.length > 0) { + return false; + } + return true; +} + +export function isDraftForwardable(draft: MessageForwardDraft): boolean { + const { attachments } = draft; + if (isDraftEmpty(draft)) { + return false; + } + if (attachments != null && attachments.length > 0) { + if (!attachments.every(isDownloaded)) { + return false; + } + } + return true; +} + +export function sortByMessageOrder( + items: ReadonlyArray, + getMesssage: ( + item: T + ) => Pick +): Array { + return orderBy( + items, + [item => getMesssage(item).received_at, item => getMesssage(item).sent_at], + ['ASC', 'ASC'] + ); +} diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 5b688a83e4a4..8f81b2df3f97 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -15,25 +15,6 @@ "updated": "2018-09-18T19:19:27.699Z", "reasonDetail": "Part of runtime library for C++ transpiled code" }, - { - "rule": "React-useRef", - "path": "node_modules/@design-systems/utils/dist/cjs/utils/focus-lock.js", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" - }, - { - "rule": "React-useRef", - "path": "node_modules/@design-systems/utils/dist/esm/utils/focus-lock.js", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" - }, - { - "rule": "React-useRef", - "path": "node_modules/@design-systems/utils/src/utils/focus-lock.tsx", - "line": " const trap = React.useRef(null);", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" - }, { "rule": "React-createRef", "path": "node_modules/@indutny/frameless-titlebar/dist/index.es.js", @@ -188,6 +169,13 @@ "reasonCategory": "usageTrusted", "updated": "2022-06-06T22:58:37.359Z" }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/@ndelangen/get-tarball/dist/index.js", + "line": " \"innerHTML\",", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, { "rule": "eval", "path": "node_modules/@protobufjs/inquire/index.js", @@ -196,6 +184,188 @@ "updated": "2018-09-18T19:19:27.699Z", "reasonDetail": "What's being eval'd is a static string" }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-collection/dist/index.js", + "line": " const ref = ($parcel$interopDefault($hnlpS$react)).useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-collection/dist/index.js", + "line": " const itemMap = ($parcel$interopDefault($hnlpS$react)).useRef(new Map()).current;", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-collection/dist/index.js", + "line": " const ref = ($parcel$interopDefault($hnlpS$react)).useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-dismissable-layer/dist/index.js", + "line": " const ref = $g2vWm$react.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-dismissable-layer/dist/index.js", + "line": " const isPointerInsideReactTreeRef = $g2vWm$react.useRef(false);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-dismissable-layer/dist/index.js", + "line": " const handleClickRef = $g2vWm$react.useRef(()=>{});", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-dismissable-layer/dist/index.js", + "line": " const isFocusInsideReactTreeRef = $g2vWm$react.useRef(false);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-focus-scope/dist/index.js", + "line": " const lastFocusedElementRef = $buum9$react.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-focus-scope/dist/index.js", + "line": " const focusScope = $buum9$react.useRef({", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-popper/dist/index.js", + "line": " const ref = $50Iv9$react.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-roving-focus/dist/index.js", + "line": " const ref = $9QJ9Y$react.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-roving-focus/dist/index.js", + "line": " const isClickFocusRef = $9QJ9Y$react.useRef(false);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-dangerouslySetInnerHTML", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " dangerouslySetInnerHTML: {", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const triggerPointerDownPosRef = $cg2C9$react.useRef(null); // We set this to true by default so that events bubble to forms without JS (SSR)", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const firstValidItemFoundRef = $cg2C9$react.useRef(false); // aria-hide everything except the content (better supported equivalent to setting aria-modal)", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const shouldExpandOnScrollRef = $cg2C9$react.useRef(false);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const shouldRepositionRef = $cg2C9$react.useRef(true);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const prevScrollTopRef = $cg2C9$react.useRef(0);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const autoScrollTimerRef = $cg2C9$react.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const ref = $cg2C9$react.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const searchRef = $cg2C9$react.useRef('');", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-select/dist/index.js", + "line": " const timerRef = $cg2C9$react.useRef(0);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-toggle-group/dist/index.js", + "line": " const ref = ($parcel$interopDefault($1z6X1$react)).useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-use-callback-ref/dist/index.js", + "line": " const callbackRef = $92muK$react.useRef(callback);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-use-controllable-state/dist/index.js", + "line": " const prevValueRef = $ijazI$react.useRef(value);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@radix-ui/react-use-previous/dist/index.js", + "line": " const ref = $kjM8v$react.useRef({", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, { "rule": "React-useRef", "path": "node_modules/@react-spring/animated/dist/react-spring-animated.cjs.dev.js", @@ -434,13 +604,6 @@ "reasonCategory": "falseMatch", "updated": "2022-06-23T23:21:04.555Z" }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.cjs.js", - "line": " console.log(\"Open this URL in your browser\\n\\n\" + getPlaygroundUrl(element.innerHTML));", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" - }, { "rule": "DOM-outerHTML", "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.cjs.js", @@ -469,13 +632,6 @@ "reasonCategory": "falseMatch", "updated": "2022-06-23T23:21:04.555Z" }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.esm.js", - "line": " console.log(\"Open this URL in your browser\\n\\n\" + getPlaygroundUrl(element.innerHTML));", - "reasonCategory": "usageTrusted", - "updated": "2022-06-23T23:21:04.555Z" - }, { "rule": "DOM-outerHTML", "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.esm.js", @@ -504,13 +660,6 @@ "reasonCategory": "falseMatch", "updated": "2022-06-23T23:21:04.555Z" }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.umd.js", - "line": " console.log(\"Open this URL in your browser\\n\\n\" + getPlaygroundUrl(element.innerHTML));", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" - }, { "rule": "DOM-outerHTML", "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.umd.js", @@ -565,61 +714,89 @@ "reasonCategory": "testCode", "updated": "2022-06-23T23:21:04.555Z" }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.cjs.js", + "line": " const playgroundUrl = getPlaygroundUrl(element.innerHTML);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.esm.js", + "line": " const playgroundUrl = getPlaygroundUrl(element.innerHTML);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/@testing-library/dom/dist/@testing-library/dom.umd.js", + "line": "\t const playgroundUrl = getPlaygroundUrl(element.innerHTML);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, { "rule": "DOM-innerHTML", "path": "node_modules/@testing-library/dom/dist/screen.js", - "line": " console.log(`Open this URL in your browser\\n\\n${getPlaygroundUrl(element.innerHTML)}`);", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" + "line": " const playgroundUrl = getPlaygroundUrl(element.innerHTML);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" }, { "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/jest-dom/dist/to-be-empty-dom-element.js", - "line": " return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeEmptyDOMElement`, 'element', ''), '', 'Received:', ` ${this.utils.printReceived(element.innerHTML)}`].join('\\n');", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" - }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/jest-dom/dist/to-be-empty.js", + "path": "node_modules/@testing-library/jest-dom/dist/matchers-d012a0f1.js", "line": " pass: element.innerHTML === '',", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" }, { "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/jest-dom/dist/to-be-empty.js", - "line": " return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeEmpty`, 'element', ''), '', 'Received:', ` ${this.utils.printReceived(element.innerHTML)}`].join('\\n');", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" + "path": "node_modules/@testing-library/jest-dom/dist/matchers-d012a0f1.js", + "line": " ` ${this.utils.printReceived(element.innerHTML)}`,", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" }, { "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/jest-dom/dist/to-contain-html.js", + "path": "node_modules/@testing-library/jest-dom/dist/matchers-d012a0f1.js", + "line": " ` ${this.utils.printReceived(element.innerHTML)}`,", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/@testing-library/jest-dom/dist/matchers-d012a0f1.js", "line": " div.innerHTML = htmlText;", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" }, { "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/jest-dom/dist/to-contain-html.js", - "line": " return div.innerHTML;", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" + "path": "node_modules/@testing-library/jest-dom/dist/matchers-d012a0f1.js", + "line": " return div.innerHTML", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" }, { "rule": "DOM-outerHTML", - "path": "node_modules/@testing-library/jest-dom/dist/to-contain-html.js", + "path": "node_modules/@testing-library/jest-dom/dist/matchers-d012a0f1.js", "line": " pass: container.outerHTML.includes(getNormalizedHtml(container, htmlText)),", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" }, { "rule": "DOM-innerHTML", - "path": "node_modules/@testing-library/user-event/dist/select-options.js", - "line": " const matchingOption = allOptions.find(o => o.value === val || o.innerHTML === val);", - "reasonCategory": "testCode", - "updated": "2022-06-23T23:21:04.555Z" + "path": "node_modules/@testing-library/user-event/dist/cjs/utility/selectOptions.js", + "line": " const matchingOption = allOptions.find((o)=>o.value === val || o.innerHTML === val);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/@testing-library/user-event/dist/esm/utility/selectOptions.js", + "line": " const matchingOption = allOptions.find((o)=>o.value === val || o.innerHTML === val);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" }, { "rule": "eval", @@ -696,13 +873,6 @@ "reasonCategory": "falseMatch", "updated": "2018-09-15T00:38:04.183Z" }, - { - "rule": "thenify-multiArgs", - "path": "node_modules/default-browser-id/node_modules/pify/index.js", - "line": "\t\t\t\t} else if (opts.multiArgs) {", - "reasonCategory": "usageTrusted", - "updated": "2023-04-20T16:43:40.643Z" - }, { "rule": "DOM-outerHTML", "path": "node_modules/domutils/node_modules/dom-serializer/lib/esm/index.js", @@ -717,40 +887,6 @@ "reasonCategory": "falseMatch", "updated": "2023-01-11T23:51:00.603Z" }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/element-resize-detector/dist/element-resize-detector.js", - "line": " div.innerHTML = \"\";", - "reasonCategory": "usageTrusted", - "updated": "2022-06-01T22:57:44.591Z" - }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/element-resize-detector/dist/element-resize-detector.js", - "line": " styleElement.innerHTML = style;", - "reasonCategory": "usageTrusted", - "updated": "2022-06-01T22:57:44.591Z" - }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/element-resize-detector/dist/element-resize-detector.min.js", - "reasonCategory": "usageTrusted", - "updated": "2022-06-01T22:57:44.591Z" - }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/element-resize-detector/src/browser-detector.js", - "line": " div.innerHTML = \"\";", - "reasonCategory": "usageTrusted", - "updated": "2022-06-01T22:57:44.591Z" - }, - { - "rule": "DOM-innerHTML", - "path": "node_modules/element-resize-detector/src/detection-strategy/scroll.js", - "line": " styleElement.innerHTML = style;", - "reasonCategory": "usageTrusted", - "updated": "2022-06-01T22:57:44.591Z" - }, { "rule": "React-ref", "path": "node_modules/esquery/dist/esquery.esm.min.js", @@ -765,6 +901,95 @@ "updated": "2020-08-26T00:10:28.628Z", "reasonDetail": "isn't react" }, + { + "rule": "eval", + "path": "node_modules/expect-playwright/lib/matchers/toHaveSelectorCount/index.js", + "line": " const actualCount = await elementHandle.$$eval(selector, (el) => el.length);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.esm.js", + "line": " const ref = React.useRef(value);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.esm.js", + "line": " const referenceRef = React.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.esm.js", + "line": " const floatingRef = React.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.esm.js", + "line": " const dataRef = React.useRef(data);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.esm.js", + "line": " const isMountedRef = React.useRef(false);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.esm.min.js", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.umd.js", + "line": " const ref = React__namespace.useRef(value);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.umd.js", + "line": " const referenceRef = React__namespace.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.umd.js", + "line": " const floatingRef = React__namespace.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.umd.js", + "line": " const dataRef = React__namespace.useRef(data);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.umd.js", + "line": " const isMountedRef = React__namespace.useRef(false);", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.umd.min.js", + "reasonCategory": "usageTrusted", + "updated": "2023-10-03T18:55:06.301Z" + }, { "rule": "React-findDOMNode", "path": "node_modules/focus-trap-react/dist/focus-trap-react.js", @@ -852,6 +1077,190 @@ "updated": "2022-07-26T23:41:36.800Z", "reasonDetail": "Part of keyword list for preservation in final minified build" }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/tocbot/dist/tocbot.js", + "line": " tocElement.innerHTML = ''", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/tocbot/dist/tocbot.min.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/tocbot/out/_next/static/chunks/253-af2be75f8688092d.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-dangerouslySetInnerHTML", + "path": "node_modules/tocbot/out/_next/static/chunks/253-af2be75f8688092d.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-ref", + "path": "node_modules/tocbot/out/_next/static/chunks/684-c4d85164cfbebace.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/tocbot/out/_next/static/chunks/684-c4d85164cfbebace.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-dangerouslySetInnerHTML", + "path": "node_modules/tocbot/out/_next/static/chunks/894.d8d3be35b7c84b33.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-ref", + "path": "node_modules/tocbot/out/_next/static/chunks/894.d8d3be35b7c84b33.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/tocbot/out/_next/static/chunks/framework-3911a61406e859ea.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-dangerouslySetInnerHTML", + "path": "node_modules/tocbot/out/_next/static/chunks/framework-3911a61406e859ea.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-ref", + "path": "node_modules/tocbot/out/_next/static/chunks/framework-3911a61406e859ea.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/tocbot/out/_next/static/chunks/framework-3911a61406e859ea.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/tocbot/out/_next/static/chunks/main-292648d00afc3512.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-dangerouslySetInnerHTML", + "path": "node_modules/tocbot/out/_next/static/chunks/main-292648d00afc3512.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-ref", + "path": "node_modules/tocbot/out/_next/static/chunks/main-292648d00afc3512.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/tocbot/out/_next/static/chunks/main-292648d00afc3512.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-dangerouslySetInnerHTML", + "path": "node_modules/tocbot/out/_next/static/chunks/pages/_error-b0eae765db80170a.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-ref", + "path": "node_modules/tocbot/out/_next/static/chunks/pages/_error-b0eae765db80170a.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/tocbot/out/static/js/tocbot.js", + "line": " tocElement.innerHTML = ''", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "DOM-innerHTML", + "path": "node_modules/tocbot/out/static/js/tocbot.min.js", + "reasonCategory": "notExercisedByOurApp", + "updated": "2023-10-03T18:55:06.301Z" + }, + { + "rule": "React-dangerouslySetInnerHTML", + "path": "node_modules/tocbot/src/components/Template/Tracking/index.js", + "line": "