MM-52265: Boards ESLint improvements (#23218)
* eslint changes * sync max-lines with channels rule * eslint fixes * remove dupe rules
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
b5ce8ac741
Коммит
b43a74808d
@@ -11,7 +11,8 @@
|
||||
"formatjs",
|
||||
"unused-imports",
|
||||
"no-relative-import-paths",
|
||||
"import-newlines"
|
||||
"import-newlines",
|
||||
"eslint-comments"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"settings": {
|
||||
@@ -27,21 +28,101 @@
|
||||
"line",
|
||||
" Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n See LICENSE.txt for license information."
|
||||
],
|
||||
"max-lines": "off",
|
||||
"no-unused-expressions": 0,
|
||||
"babel/no-unused-expressions": [
|
||||
"array-bracket-spacing": [
|
||||
2,
|
||||
{
|
||||
"allowShortCircuit": true
|
||||
}
|
||||
"never"
|
||||
],
|
||||
"eol-last": [
|
||||
"array-callback-return": 2,
|
||||
"arrow-body-style": 0,
|
||||
"arrow-parens": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"import/no-unresolved": 2,
|
||||
"import/order": [
|
||||
"arrow-spacing": [
|
||||
2,
|
||||
{
|
||||
"before": true,
|
||||
"after": true
|
||||
}
|
||||
],
|
||||
"block-scoped-var": 2,
|
||||
"brace-style": [
|
||||
2,
|
||||
"1tbs",
|
||||
{
|
||||
"allowSingleLine": false
|
||||
}
|
||||
],
|
||||
"capitalized-comments": 0,
|
||||
"class-methods-use-this": 0,
|
||||
"comma-dangle": [
|
||||
2,
|
||||
"always-multiline"
|
||||
],
|
||||
"comma-spacing": [
|
||||
2,
|
||||
{
|
||||
"before": false,
|
||||
"after": true
|
||||
}
|
||||
],
|
||||
"comma-style": [
|
||||
2,
|
||||
"last"
|
||||
],
|
||||
"complexity": [
|
||||
0,
|
||||
10
|
||||
],
|
||||
"computed-property-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"consistent-return": 2,
|
||||
"consistent-this": [
|
||||
2,
|
||||
"self"
|
||||
],
|
||||
"constructor-super": 2,
|
||||
"curly": [
|
||||
2,
|
||||
"all"
|
||||
],
|
||||
"dot-location": [
|
||||
2,
|
||||
"property"
|
||||
],
|
||||
"dot-notation": 2,
|
||||
"eqeqeq": [
|
||||
2,
|
||||
"smart"
|
||||
],
|
||||
"func-call-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"func-name-matching": 0,
|
||||
"func-names": 2,
|
||||
"func-style": [
|
||||
2,
|
||||
"declaration",
|
||||
{
|
||||
"allowArrowFunctions": true
|
||||
}
|
||||
],
|
||||
"generator-star-spacing": [
|
||||
2,
|
||||
{
|
||||
"before": false,
|
||||
"after": true
|
||||
}
|
||||
],
|
||||
"global-require": 2,
|
||||
"guard-for-in": 2,
|
||||
"id-blacklist": 0,
|
||||
"import/no-unresolved": 0, // ts handles this better
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"newlines-between": "always-and-inside-groups",
|
||||
"groups": [
|
||||
@@ -56,27 +137,413 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"indent": 0, // ts handles this
|
||||
"jsx-quotes": [
|
||||
2,
|
||||
"prefer-single"
|
||||
],
|
||||
"key-spacing": [
|
||||
2,
|
||||
{
|
||||
"beforeColon": false,
|
||||
"afterColon": true,
|
||||
"mode": "strict"
|
||||
}
|
||||
],
|
||||
"keyword-spacing": [
|
||||
2,
|
||||
{
|
||||
"before": true,
|
||||
"after": true,
|
||||
"overrides": {}
|
||||
}
|
||||
],
|
||||
"line-comment-position": 0,
|
||||
"linebreak-style": 2,
|
||||
"lines-around-comment": [
|
||||
2,
|
||||
{
|
||||
"beforeBlockComment": true,
|
||||
"beforeLineComment": true,
|
||||
"allowBlockStart": true,
|
||||
"allowBlockEnd": true
|
||||
}
|
||||
],
|
||||
"max-lines": [
|
||||
2,
|
||||
{
|
||||
"max": 800,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
],
|
||||
"max-statements-per-line": [
|
||||
2,
|
||||
{
|
||||
"max": 1
|
||||
}
|
||||
],
|
||||
"multiline-ternary": [
|
||||
1,
|
||||
"never"
|
||||
],
|
||||
"new-cap": 2,
|
||||
"new-parens": 2,
|
||||
"padding-line-between-statements": [
|
||||
2,
|
||||
{
|
||||
"blankLine": "always",
|
||||
"prev": "*",
|
||||
"next": "return"
|
||||
}
|
||||
],
|
||||
"newline-per-chained-call": 0,
|
||||
"no-alert": 2,
|
||||
"no-array-constructor": 2,
|
||||
"no-await-in-loop": 2,
|
||||
"no-caller": 2,
|
||||
"no-case-declarations": 2,
|
||||
"no-class-assign": 2,
|
||||
"no-compare-neg-zero": 2,
|
||||
"no-cond-assign": [
|
||||
2,
|
||||
"except-parens"
|
||||
],
|
||||
"no-confusing-arrow": 2,
|
||||
"no-console": 2,
|
||||
"no-const-assign": 2,
|
||||
"no-constant-condition": 2,
|
||||
"no-debugger": 2,
|
||||
"no-div-regex": 2,
|
||||
"no-dupe-args": 2,
|
||||
"no-dupe-class-members": 2,
|
||||
"no-dupe-keys": 2,
|
||||
"no-duplicate-case": 2,
|
||||
"no-duplicate-imports": [
|
||||
2,
|
||||
{
|
||||
"includeExports": true
|
||||
}
|
||||
],
|
||||
"no-else-return": 2,
|
||||
"no-empty": 2,
|
||||
"no-empty-function": 2,
|
||||
"no-empty-pattern": 2,
|
||||
"no-eval": 2,
|
||||
"no-ex-assign": 2,
|
||||
"no-extend-native": 2,
|
||||
"no-extra-bind": 2,
|
||||
"no-extra-label": 2,
|
||||
"no-extra-parens": 0,
|
||||
"no-extra-semi": 2,
|
||||
"no-fallthrough": 2,
|
||||
"no-floating-decimal": 2,
|
||||
"no-func-assign": 2,
|
||||
"no-global-assign": 2,
|
||||
"no-implicit-coercion": 2,
|
||||
"no-implicit-globals": 0,
|
||||
"no-implied-eval": 2,
|
||||
"no-inner-declarations": 0,
|
||||
"no-invalid-regexp": 2,
|
||||
"no-irregular-whitespace": 2,
|
||||
"no-iterator": 2,
|
||||
"no-labels": 2,
|
||||
"no-lone-blocks": 2,
|
||||
"no-lonely-if": 2,
|
||||
"no-loop-func": 2,
|
||||
"no-magic-numbers": [
|
||||
0,
|
||||
{
|
||||
"ignore": [
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
"enforceConst": true,
|
||||
"detectObjects": true
|
||||
}
|
||||
],
|
||||
"no-mixed-operators": [
|
||||
2,
|
||||
{
|
||||
"allowSamePrecedence": false
|
||||
}
|
||||
],
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-multi-assign": 2,
|
||||
"no-multi-spaces": [
|
||||
2,
|
||||
{
|
||||
"exceptions": {
|
||||
"Property": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"no-multi-str": 0,
|
||||
"no-multiple-empty-lines": [
|
||||
2,
|
||||
{
|
||||
"max": 1
|
||||
}
|
||||
],
|
||||
"no-native-reassign": 2,
|
||||
"no-negated-condition": 2,
|
||||
"no-nested-ternary": 2,
|
||||
"no-new": 2,
|
||||
"no-new-func": 2,
|
||||
"no-new-object": 2,
|
||||
"no-new-symbol": 2,
|
||||
"no-new-wrappers": 2,
|
||||
"no-octal-escape": 2,
|
||||
"no-param-reassign": 2,
|
||||
"no-process-env": 2,
|
||||
"no-process-exit": 2,
|
||||
"no-proto": 2,
|
||||
"no-redeclare": 2,
|
||||
"no-return-assign": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"no-return-await": 2,
|
||||
"no-script-url": 2,
|
||||
"no-self-assign": [
|
||||
2,
|
||||
{
|
||||
"props": true
|
||||
}
|
||||
],
|
||||
"no-self-compare": 2,
|
||||
"no-sequences": 2,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-spaced-func": 2,
|
||||
"no-tabs": 0,
|
||||
"no-template-curly-in-string": 2,
|
||||
"no-ternary": 0,
|
||||
"no-this-before-super": 2,
|
||||
"no-throw-literal": 2,
|
||||
"no-trailing-spaces": [
|
||||
2,
|
||||
{
|
||||
"skipBlankLines": false
|
||||
}
|
||||
],
|
||||
"no-undef-init": 2,
|
||||
"no-undefined": 0,
|
||||
"react/jsx-filename-extension": 0,
|
||||
"no-underscore-dangle": 2,
|
||||
"no-unexpected-multiline": 2,
|
||||
"no-unmodified-loop-condition": 2,
|
||||
"no-unneeded-ternary": [
|
||||
2,
|
||||
{
|
||||
"defaultAssignment": false
|
||||
}
|
||||
],
|
||||
"no-unreachable": 2,
|
||||
"no-unsafe-finally": 2,
|
||||
"no-unsafe-negation": 2,
|
||||
"no-unused-expressions": 2,
|
||||
"no-unused-vars": [
|
||||
2,
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "after-used"
|
||||
}
|
||||
],
|
||||
"no-use-before-define": 0,
|
||||
"no-useless-computed-key": 2,
|
||||
"no-useless-concat": 2,
|
||||
"no-useless-constructor": 2,
|
||||
"no-useless-escape": 2,
|
||||
"no-useless-rename": 2,
|
||||
"no-useless-return": 2,
|
||||
"no-var": 0,
|
||||
"no-void": 2,
|
||||
"no-warning-comments": 1,
|
||||
"no-whitespace-before-property": 2,
|
||||
"no-with": 2,
|
||||
"object-curly-newline": 0,
|
||||
"object-curly-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"object-property-newline": [
|
||||
2,
|
||||
{
|
||||
"allowMultiplePropertiesPerLine": true
|
||||
}
|
||||
],
|
||||
"object-shorthand": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"one-var": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"one-var-declaration-per-line": 0,
|
||||
"operator-assignment": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"operator-linebreak": [
|
||||
2,
|
||||
"after"
|
||||
],
|
||||
"padded-blocks": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"prefer-arrow-callback": 2,
|
||||
"prefer-const": 2,
|
||||
"prefer-destructuring": 0,
|
||||
"prefer-numeric-literals": 2,
|
||||
"prefer-promise-reject-errors": 2,
|
||||
"prefer-rest-params": 2,
|
||||
"prefer-spread": 2,
|
||||
"prefer-template": 0,
|
||||
"quote-props": [
|
||||
2,
|
||||
"as-needed"
|
||||
],
|
||||
"quotes": [
|
||||
2,
|
||||
"single",
|
||||
"avoid-escape"
|
||||
],
|
||||
"radix": 2,
|
||||
"react/display-name": [
|
||||
0,
|
||||
{
|
||||
"ignoreTranspilerName": false
|
||||
}
|
||||
],
|
||||
"react/forbid-component-props": 0,
|
||||
"react/forbid-elements": [
|
||||
2,
|
||||
{
|
||||
"forbid": [
|
||||
"embed"
|
||||
]
|
||||
}
|
||||
],
|
||||
"react/jsx-boolean-value": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"react/jsx-closing-bracket-location": [
|
||||
2,
|
||||
{
|
||||
"location": "tag-aligned"
|
||||
}
|
||||
],
|
||||
"react/jsx-curly-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"react/jsx-equals-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"react/jsx-filename-extension": 2,
|
||||
"react/jsx-first-prop-new-line": [
|
||||
2,
|
||||
"multiline"
|
||||
],
|
||||
"react/jsx-handler-names": 0,
|
||||
"react/jsx-indent": [
|
||||
2,
|
||||
4
|
||||
],
|
||||
"react/jsx-indent-props": [
|
||||
2,
|
||||
4
|
||||
],
|
||||
"react/jsx-key": 2,
|
||||
"react/jsx-max-props-per-line": [
|
||||
2,
|
||||
{
|
||||
"maximum": 1
|
||||
}
|
||||
],
|
||||
"react/jsx-no-bind": 0,
|
||||
"react/jsx-no-comment-textnodes": 2,
|
||||
"react/jsx-no-duplicate-props": [
|
||||
2,
|
||||
{
|
||||
"ignoreCase": false
|
||||
}
|
||||
],
|
||||
"react/jsx-no-literals": 2,
|
||||
"react/jsx-no-target-blank": 2,
|
||||
"react/jsx-no-undef": 2,
|
||||
"react/jsx-pascal-case": 2,
|
||||
"react/jsx-tag-spacing": [
|
||||
2,
|
||||
{
|
||||
"closingSlash": "never",
|
||||
"beforeSelfClosing": "never",
|
||||
"afterOpening": "never"
|
||||
}
|
||||
],
|
||||
"react/jsx-uses-react": 2,
|
||||
"react/jsx-uses-vars": 2,
|
||||
"react/jsx-wrap-multilines": 2,
|
||||
"react/no-array-index-key": 1,
|
||||
"react/no-children-prop": 2,
|
||||
"react/no-danger": 0,
|
||||
"react/no-danger-with-children": 2,
|
||||
"react/no-deprecated": 1,
|
||||
"react/no-did-mount-set-state": 2,
|
||||
"react/no-did-update-set-state": 2,
|
||||
"react/no-direct-mutation-state": 2,
|
||||
"react/no-find-dom-node": 1,
|
||||
"react/no-is-mounted": 2,
|
||||
"react/no-multi-comp": [
|
||||
2,
|
||||
{
|
||||
"ignoreStateless": true
|
||||
}
|
||||
],
|
||||
"react/no-render-return-value": 2,
|
||||
"react/no-set-state": 0,
|
||||
"react/no-string-refs": 0,
|
||||
"react/no-unescaped-entities": 2,
|
||||
"react/no-unknown-property": 2,
|
||||
"react/no-unused-prop-types": [
|
||||
1,
|
||||
{
|
||||
"skipShapeProps": true
|
||||
}
|
||||
],
|
||||
"react/prefer-es6-class": 2,
|
||||
"react/prefer-stateless-function": 2,
|
||||
"react/prop-types": [
|
||||
2,
|
||||
{
|
||||
"ignore": [
|
||||
"location",
|
||||
"history",
|
||||
"component"
|
||||
"component",
|
||||
"className"
|
||||
]
|
||||
}
|
||||
],
|
||||
"react/no-string-refs": 2,
|
||||
"no-only-tests/no-only-tests": [
|
||||
2,
|
||||
{
|
||||
"focus": [
|
||||
"only",
|
||||
"skip"
|
||||
]
|
||||
}
|
||||
"react/require-default-props": 0,
|
||||
"react/require-optimization": 1,
|
||||
"react/require-render-return": 2,
|
||||
"react/self-closing-comp": 2,
|
||||
"react/sort-comp": 0,
|
||||
"react/style-prop-object": [
|
||||
2,
|
||||
{
|
||||
"allow": [
|
||||
"FormattedNumber",
|
||||
"FormattedDuration",
|
||||
"FormattedRelativeTime",
|
||||
"Timestamp"
|
||||
]
|
||||
}
|
||||
],
|
||||
"max-nested-callbacks": [
|
||||
2,
|
||||
@@ -95,11 +562,84 @@
|
||||
2,
|
||||
3
|
||||
],
|
||||
"object-curly-spacing": [
|
||||
"formatjs/no-multiple-whitespaces": 2,
|
||||
"require-yield": 2,
|
||||
"rest-spread-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"formatjs/no-multiple-whitespaces": 2
|
||||
"semi": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"semi-spacing": [
|
||||
2,
|
||||
{
|
||||
"before": false,
|
||||
"after": true
|
||||
}
|
||||
],
|
||||
"sort-imports": [
|
||||
2,
|
||||
{
|
||||
"ignoreDeclarationSort": true
|
||||
}
|
||||
],
|
||||
"sort-keys": 0,
|
||||
"space-before-blocks": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"space-before-function-paren": [
|
||||
2,
|
||||
{
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}
|
||||
],
|
||||
"space-in-parens": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"space-infix-ops": 2,
|
||||
"space-unary-ops": [
|
||||
2,
|
||||
{
|
||||
"words": true,
|
||||
"nonwords": false
|
||||
}
|
||||
],
|
||||
"symbol-description": 2,
|
||||
"template-curly-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"valid-typeof": [
|
||||
2,
|
||||
{
|
||||
"requireStringLiterals": false
|
||||
}
|
||||
],
|
||||
"vars-on-top": 0,
|
||||
"wrap-iife": [
|
||||
2,
|
||||
"outside"
|
||||
],
|
||||
"wrap-regex": 2,
|
||||
"yoda": [
|
||||
2,
|
||||
"never",
|
||||
{
|
||||
"exceptRange": false,
|
||||
"onlyEquality": false
|
||||
}
|
||||
],
|
||||
"eol-last": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"eslint-comments/no-unused-disable": 2
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
@@ -206,7 +746,9 @@
|
||||
"global-require": 0,
|
||||
"new-cap": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-import-assign": 0
|
||||
"no-import-assign": 0,
|
||||
"no-console": 0,
|
||||
"max-lines": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,63 +5,63 @@
|
||||
|
||||
const config = {
|
||||
transform: {
|
||||
"^.+\\.(t|j)sx?$": ["@swc/jest"]
|
||||
'^.+\\.(t|j)sx?$': ['@swc/jest'],
|
||||
},
|
||||
moduleFileExtensions: [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
'ts',
|
||||
'tsx',
|
||||
'js',
|
||||
'jsx',
|
||||
'json',
|
||||
'node',
|
||||
],
|
||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||
transformIgnorePatterns: [
|
||||
"/nanoevents/",
|
||||
"node_modules/(?!react-native|react-router|react-day-picker)"
|
||||
'/nanoevents/',
|
||||
'node_modules/(?!react-native|react-router|react-day-picker)',
|
||||
],
|
||||
testEnvironment: "jsdom",
|
||||
testEnvironment: 'jsdom',
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
"src/**/*.{ts,tsx,js,jsx}",
|
||||
"!src/test/**"
|
||||
'src/**/*.{ts,tsx,js,jsx}',
|
||||
'!src/test/**',
|
||||
],
|
||||
testPathIgnorePatterns: [
|
||||
"/node_modules/",
|
||||
'/node_modules/',
|
||||
],
|
||||
clearMocks: true,
|
||||
coverageReporters: [
|
||||
"lcov",
|
||||
"text-summary"
|
||||
'lcov',
|
||||
'text-summary',
|
||||
],
|
||||
moduleNameMapper: {
|
||||
"^.+\\.(scss|css)$": "<rootDir>/src/test/style_mock.json",
|
||||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
|
||||
"\\.(scss|css)$": "<rootDir>/__mocks__/styleMock.js",
|
||||
"^bundle-loader\\?lazy\\!(.*)$": "$1",
|
||||
"^src(.*)$": "<rootDir>/src$1",
|
||||
"^i18n(.*)$": "<rootDir>/i18n$1",
|
||||
"^static(.*)$": "<rootDir>/static$1",
|
||||
"^moment(.*)$": "<rootDir>/../node_modules/moment$1",
|
||||
'^.+\\.(scss|css)$': '<rootDir>/src/test/style_mock.json',
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/fileMock.js',
|
||||
'\\.(scss|css)$': '<rootDir>/__mocks__/styleMock.js',
|
||||
'^bundle-loader\\?lazy\\!(.*)$': '$1',
|
||||
'^src(.*)$': '<rootDir>/src$1',
|
||||
'^i18n(.*)$': '<rootDir>/i18n$1',
|
||||
'^static(.*)$': '<rootDir>/static$1',
|
||||
'^moment(.*)$': '<rootDir>/../node_modules/moment$1',
|
||||
},
|
||||
moduleDirectories: [
|
||||
"src",
|
||||
"node_modules",
|
||||
'src',
|
||||
'node_modules',
|
||||
],
|
||||
reporters: [
|
||||
"default",
|
||||
"jest-junit"
|
||||
'default',
|
||||
'jest-junit',
|
||||
],
|
||||
setupFiles: [
|
||||
"jest-canvas-mock"
|
||||
'jest-canvas-mock',
|
||||
],
|
||||
setupFilesAfterEnv: [
|
||||
"<rootDir>/src/test/setup.tsx"
|
||||
'<rootDir>/src/test/setup.tsx',
|
||||
],
|
||||
testTimeout: 60000,
|
||||
testEnvironmentOptions: {
|
||||
url: "http://localhost:8065"
|
||||
}
|
||||
url: 'http://localhost:8065',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
||||
@@ -19,5 +19,6 @@ module.exports = function loader(source) {
|
||||
newSource.push(line);
|
||||
}
|
||||
});
|
||||
|
||||
return newSource.join('\n');
|
||||
};
|
||||
|
||||
@@ -106,6 +106,8 @@
|
||||
"css-loader": "6.7.1",
|
||||
"eslint-import-resolver-webpack": "0.13.2",
|
||||
"eslint-plugin-babel": "^5.3.1",
|
||||
"eslint-plugin-eslint-comments": "3.2.0",
|
||||
"eslint-plugin-formatjs": "4.9.0",
|
||||
"eslint-plugin-header": "3.1.1",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint-plugin-import-newlines": "1.3.1",
|
||||
|
||||
@@ -14,8 +14,8 @@ import FlashMessages from './components/flashMessages'
|
||||
import NewVersionBanner from './components/newVersionBanner'
|
||||
import {Utils} from './utils'
|
||||
import {fetchMe, getMe} from './store/users'
|
||||
import {getLanguage, fetchLanguage} from './store/language'
|
||||
import {useAppSelector, useAppDispatch} from './store/hooks'
|
||||
import {fetchLanguage, getLanguage} from './store/language'
|
||||
import {useAppDispatch, useAppSelector} from './store/hooks'
|
||||
import {fetchClientConfig} from './store/clientConfig'
|
||||
import FocalboardRouter from './router'
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ class Archiver {
|
||||
private static exportArchive(prom: Promise<Response>): void {
|
||||
// TODO: don't download whole archive before presenting SaveAs dialog.
|
||||
prom.then((response) => {
|
||||
response.blob().
|
||||
then((blob) => {
|
||||
response.blob()
|
||||
.then((blob) => {
|
||||
const link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ class BlockIcons {
|
||||
randomIcon(): string {
|
||||
const index = Math.floor(Math.random() * randomEmojiList.length)
|
||||
const icon = randomEmojiList[index]
|
||||
|
||||
return icon
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
import {createPatchesFromBlocks, createBlock} from './block'
|
||||
import {createBlock, createPatchesFromBlocks} from './block'
|
||||
|
||||
describe('block tests', () => {
|
||||
const board = TestBlockFactory.createBoard()
|
||||
|
||||
@@ -53,6 +53,7 @@ interface FileInfo {
|
||||
|
||||
function createBlock(block?: Block): Block {
|
||||
const now = Date.now()
|
||||
|
||||
return {
|
||||
id: block?.id || Utils.createGuid(Utils.blockTypeToIDType(block?.type)),
|
||||
schema: 1,
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
import {
|
||||
createPatchesFromBoards,
|
||||
createBoard,
|
||||
IPropertyTemplate,
|
||||
createPatchesFromBoardsAndBlocks
|
||||
createBoard,
|
||||
createPatchesFromBoards,
|
||||
createPatchesFromBoardsAndBlocks,
|
||||
} from './board'
|
||||
import {createBlock} from './block'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import difference from 'lodash/difference'
|
||||
|
||||
import {Utils, IDType} from 'src/utils'
|
||||
import {IDType, Utils} from 'src/utils'
|
||||
|
||||
import {Block, BlockPatch, createPatchesFromBlocks} from './block'
|
||||
import {Card} from './card'
|
||||
@@ -54,7 +54,7 @@ type BoardPatch = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
updatedProperties?: Record<string, any>
|
||||
deletedProperties?: string[]
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
updatedCardProperties?: IPropertyTemplate[]
|
||||
deletedCardProperties?: string[]
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ function createCard(block?: Block): Card {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...createBlock(block),
|
||||
type: 'card',
|
||||
|
||||
@@ -21,9 +21,11 @@ function createFilterGroup(o?: FilterGroup): FilterGroup {
|
||||
if (isAFilterGroupInstance(p)) {
|
||||
return createFilterGroup(p)
|
||||
}
|
||||
|
||||
return createFilterClause(p)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
operation: o?.operation || 'and',
|
||||
filters,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Card} from './blocks/card'
|
||||
import {IPropertyTemplate, IPropertyOption, BoardGroup} from './blocks/board'
|
||||
import {BoardGroup, IPropertyOption, IPropertyTemplate} from './blocks/board'
|
||||
|
||||
function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty?: IPropertyTemplate): BoardGroup[] {
|
||||
const groups = []
|
||||
@@ -22,6 +22,7 @@ function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty
|
||||
// Empty group
|
||||
const emptyGroupCards = cards.filter((card) => {
|
||||
const groupByOptionId = card.fields.properties[groupByProperty?.id || '']
|
||||
|
||||
return !groupByOptionId || !groupByProperty?.options.find((option) => option.id === groupByOptionId)
|
||||
})
|
||||
const group: BoardGroup = {
|
||||
@@ -31,15 +32,16 @@ function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty
|
||||
groups.push(group)
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
function getOptionGroups(cards: Card[], visibleOptionIds: string[], hiddenOptionIds: string[], groupByProperty?: IPropertyTemplate): {visible: BoardGroup[], hidden: BoardGroup[]} {
|
||||
let unassignedOptionIds: string[] = []
|
||||
if (groupByProperty) {
|
||||
unassignedOptionIds = groupByProperty.options.
|
||||
filter((o: IPropertyOption) => !visibleOptionIds.includes(o.id) && !hiddenOptionIds.includes(o.id)).
|
||||
map((o: IPropertyOption) => o.id)
|
||||
unassignedOptionIds = groupByProperty.options
|
||||
.filter((o: IPropertyOption) => !visibleOptionIds.includes(o.id) && !hiddenOptionIds.includes(o.id))
|
||||
.map((o: IPropertyOption) => o.id)
|
||||
}
|
||||
const allVisibleOptionIds = [...visibleOptionIds, ...unassignedOptionIds]
|
||||
|
||||
@@ -50,6 +52,7 @@ function getOptionGroups(cards: Card[], visibleOptionIds: string[], hiddenOption
|
||||
|
||||
const visibleGroups = groupCardsByOptions(cards, allVisibleOptionIds, groupByProperty)
|
||||
const hiddenGroups = groupCardsByOptions(cards, hiddenOptionIds, groupByProperty)
|
||||
|
||||
return {visible: visibleGroups, hidden: hiddenGroups}
|
||||
}
|
||||
export function getVisibleAndHiddenGroups(cards: Card[], visibleOptionIds: string[], hiddenOptionIds: string[], groupByProperty?: IPropertyTemplate): {visible: BoardGroup[], hidden: BoardGroup[]} {
|
||||
@@ -70,6 +73,7 @@ function getPersonGroups(cards: Card[], groupByProperty: IPropertyTemplate, hidd
|
||||
}
|
||||
|
||||
const curGroup = unique[key] ?? []
|
||||
|
||||
return {...unique, [key]: [...curGroup, item]}
|
||||
}, {})
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ class CardFilter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dateProperty
|
||||
}
|
||||
|
||||
@@ -51,6 +52,7 @@ class CardFilter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
Utils.assert(filterGroup.operation === 'and')
|
||||
@@ -63,6 +65,7 @@ class CardFilter {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -95,12 +98,14 @@ class CardFilter {
|
||||
if (filter.values?.length < 1) {
|
||||
break
|
||||
} // No values = ignore clause (always met)
|
||||
|
||||
return (filter.values.find((cValue) => (Array.isArray(value) ? value.includes(cValue) : cValue === value)) !== undefined)
|
||||
}
|
||||
case 'notIncludes': {
|
||||
if (filter.values?.length < 1) {
|
||||
break
|
||||
} // No values = ignore clause (always met)
|
||||
|
||||
return (filter.values.find((cValue) => (Array.isArray(value) ? value.includes(cValue) : cValue === value)) === undefined)
|
||||
}
|
||||
case 'isEmpty': {
|
||||
@@ -128,50 +133,59 @@ class CardFilter {
|
||||
if (dateValue.from) {
|
||||
return dateValue.from > (numericFilter - halfDay) && dateValue.from < (numericFilter + halfDay)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if (dateValue.from && dateValue.to) {
|
||||
return dateValue.from <= numericFilter && dateValue.to >= numericFilter
|
||||
}
|
||||
|
||||
return dateValue.from === numericFilter
|
||||
}
|
||||
|
||||
return filter.values[0]?.toLowerCase() === value
|
||||
}
|
||||
case 'contains': {
|
||||
if (filter.values.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return (value as string || '').includes(filter.values[0]?.toLowerCase())
|
||||
}
|
||||
case 'notContains': {
|
||||
if (filter.values.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return !(value as string || '').includes(filter.values[0]?.toLowerCase())
|
||||
}
|
||||
case 'startsWith': {
|
||||
if (filter.values.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return (value as string || '').startsWith(filter.values[0]?.toLowerCase())
|
||||
}
|
||||
case 'notStartsWith': {
|
||||
if (filter.values.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return !(value as string || '').startsWith(filter.values[0]?.toLowerCase())
|
||||
}
|
||||
case 'endsWith': {
|
||||
if (filter.values.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return (value as string || '').endsWith(filter.values[0]?.toLowerCase())
|
||||
}
|
||||
case 'notEndsWith': {
|
||||
if (filter.values.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return !(value as string || '').endsWith(filter.values[0]?.toLowerCase())
|
||||
}
|
||||
case 'isBefore': {
|
||||
@@ -187,11 +201,13 @@ class CardFilter {
|
||||
if (dateValue.from) {
|
||||
return dateValue.from < (numericFilter - halfDay)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return dateValue.from ? dateValue.from < numericFilter : false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
case 'isAfter': {
|
||||
@@ -207,14 +223,17 @@ class CardFilter {
|
||||
if (dateValue.from) {
|
||||
return dateValue.from > (numericFilter + halfDay)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if (dateValue.to) {
|
||||
return dateValue.to > numericFilter
|
||||
}
|
||||
|
||||
return dateValue.from ? dateValue.from > numericFilter : false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -222,6 +241,7 @@ class CardFilter {
|
||||
Utils.assertFailure(`Invalid filter condition ${filter.condition}`)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -243,6 +263,7 @@ class CardFilter {
|
||||
if (property.value) {
|
||||
result[property.id] = property.value
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -254,6 +275,7 @@ class CardFilter {
|
||||
result[property.id] = property.value
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -261,6 +283,7 @@ class CardFilter {
|
||||
const template = templates.find((o) => o.id === filterClause.propertyId)
|
||||
if (!template) {
|
||||
Utils.assertFailure(`propertyThatMeetsFilterClause. Cannot find template: ${filterClause.propertyId}`)
|
||||
|
||||
return {id: filterClause.propertyId}
|
||||
}
|
||||
|
||||
@@ -273,6 +296,7 @@ class CardFilter {
|
||||
if (filterClause.values.length < 1) {
|
||||
return {id: filterClause.propertyId}
|
||||
}
|
||||
|
||||
return {id: filterClause.propertyId, value: filterClause.values[0]}
|
||||
}
|
||||
case 'notIncludes': {
|
||||
@@ -285,8 +309,10 @@ class CardFilter {
|
||||
if (template.type === 'select') {
|
||||
if (template.options.length > 0) {
|
||||
const option = template.options[0]
|
||||
|
||||
return {id: filterClause.propertyId, value: option.id}
|
||||
}
|
||||
|
||||
return {id: filterClause.propertyId}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import React, {ReactElement, ReactNode} from 'react'
|
||||
import {render, screen, waitFor} from '@testing-library/react'
|
||||
|
||||
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import userEvent from '@testing-library/user-event'
|
||||
@@ -114,6 +113,5 @@ describe('components/addContentMenuItem', () => {
|
||||
)
|
||||
expect(console.error).toBeCalledWith(expect.stringContaining('addContentMenu, unknown content type: unknown'))
|
||||
expect(container).toMatchSnapshot()
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import React from 'react'
|
||||
import {useIntl} from 'react-intl'
|
||||
|
||||
import {BlockTypes, Block} from 'src/blocks/block'
|
||||
import {Block, BlockTypes} from 'src/blocks/block'
|
||||
import {Card} from 'src/blocks/card'
|
||||
import mutator from 'src/mutator'
|
||||
import octoClient from 'src/octoClient'
|
||||
@@ -27,6 +27,7 @@ const AddContentMenuItem = (props: Props): JSX.Element => {
|
||||
const handler = contentRegistry.getHandler(type)
|
||||
if (!handler) {
|
||||
Utils.logError(`addContentMenu, unknown content type: ${type}`)
|
||||
|
||||
return <></>
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import mutator from 'src/mutator'
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
import React from 'react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
fireEvent,
|
||||
act
|
||||
} from '@testing-library/react'
|
||||
|
||||
import {
|
||||
mockDOM,
|
||||
wrapDNDIntl,
|
||||
mockStateStore,
|
||||
setup
|
||||
setup,
|
||||
wrapDNDIntl,
|
||||
} from 'src/testUtils'
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
@@ -146,7 +146,7 @@ describe('components/blocksEditor/blockContent', () => {
|
||||
|
||||
test('should call onSave on hit enter in the input', async () => {
|
||||
const onSave = jest.fn()
|
||||
|
||||
|
||||
const {user} = setup(wrapDNDIntl(
|
||||
<ReduxProvider store={store}>
|
||||
<BlockContent
|
||||
|
||||
@@ -61,6 +61,7 @@ function BlockContent(props: Props) {
|
||||
const updatedBlock = await onSave(b)
|
||||
props.setEditing(null)
|
||||
props.setAfterBlock(updatedBlock)
|
||||
|
||||
return updatedBlock
|
||||
}}
|
||||
id={block.id}
|
||||
@@ -73,6 +74,7 @@ function BlockContent(props: Props) {
|
||||
const contentType = registry.get(block.contentType)
|
||||
if (contentType && contentType.Display) {
|
||||
const DisplayContent = contentType.Display
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={drop}
|
||||
@@ -118,6 +120,7 @@ function BlockContent(props: Props) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import octoClient from 'src/octoClient'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect, useState} from 'react'
|
||||
import React, {useEffect, useRef, useState} from 'react'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
import octoClient from 'src/octoClient'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import CheckboxBlock from '.'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect} from 'react'
|
||||
import React, {useEffect, useRef} from 'react'
|
||||
import {marked} from 'marked'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
@@ -23,6 +23,7 @@ const Checkbox: ContentType<ValueType> = {
|
||||
Display: (props: BlockInputProps<ValueType>) => {
|
||||
const renderer = new marked.Renderer()
|
||||
const html = marked(props.value.value || '', {renderer, breaks: true})
|
||||
|
||||
return (
|
||||
<div className='CheckboxView'>
|
||||
<input
|
||||
@@ -46,6 +47,7 @@ const Checkbox: ContentType<ValueType> = {
|
||||
useEffect(() => {
|
||||
ref.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='Checkbox'>
|
||||
<input
|
||||
|
||||
@@ -18,6 +18,7 @@ const Divider: ContentType = {
|
||||
useEffect(() => {
|
||||
props.onSave(props.value)
|
||||
}, [])
|
||||
|
||||
return null
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import H1Block from '.'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect} from 'react'
|
||||
import React, {useEffect, useRef} from 'react'
|
||||
import {marked} from 'marked'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
@@ -17,6 +17,7 @@ const H1: ContentType = {
|
||||
Display: (props: BlockInputProps) => {
|
||||
const renderer = new marked.Renderer()
|
||||
const html = marked('# ' + props.value, {renderer, breaks: true})
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{__html: html.trim()}}
|
||||
@@ -28,6 +29,7 @@ const H1: ContentType = {
|
||||
useEffect(() => {
|
||||
ref.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import H2Block from '.'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect} from 'react'
|
||||
import React, {useEffect, useRef} from 'react'
|
||||
import {marked} from 'marked'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
@@ -17,6 +17,7 @@ const H2: ContentType = {
|
||||
Display: (props: BlockInputProps) => {
|
||||
const renderer = new marked.Renderer()
|
||||
const html = marked('## ' + props.value, {renderer, breaks: true})
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{__html: html.trim()}}
|
||||
@@ -28,6 +29,7 @@ const H2: ContentType = {
|
||||
useEffect(() => {
|
||||
ref.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import H3Block from '.'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect} from 'react'
|
||||
import React, {useEffect, useRef} from 'react'
|
||||
import {marked} from 'marked'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
@@ -17,6 +17,7 @@ const H3: ContentType = {
|
||||
Display: (props: BlockInputProps) => {
|
||||
const renderer = new marked.Renderer()
|
||||
const html = marked('### ' + props.value, {renderer, breaks: true})
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{__html: html.trim()}}
|
||||
@@ -28,6 +29,7 @@ const H3: ContentType = {
|
||||
useEffect(() => {
|
||||
ref.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import octoClient from 'src/octoClient'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect, useState} from 'react'
|
||||
import React, {useEffect, useRef, useState} from 'react'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
import octoClient from 'src/octoClient'
|
||||
@@ -44,6 +44,7 @@ const Image: ContentType<FileInfo> = {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
Input: (props: BlockInputProps<FileInfo>) => {
|
||||
|
||||
@@ -45,6 +45,7 @@ export function isSubPrefix(text: string): boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -58,6 +59,7 @@ export function getBySlashCommandPrefix(slashCommandPrefix: string): ContentType
|
||||
return ct
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect} from 'react'
|
||||
import React, {useEffect, useRef} from 'react'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
|
||||
@@ -20,6 +20,7 @@ const ListItem: ContentType = {
|
||||
useEffect(() => {
|
||||
ref.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import ListItemBlock from '.'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect} from 'react'
|
||||
import React, {useEffect, useRef} from 'react'
|
||||
import {marked} from 'marked'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
@@ -15,6 +15,7 @@ const Quote: ContentType = {
|
||||
Display: (props: BlockInputProps) => {
|
||||
const renderer = new marked.Renderer()
|
||||
const html = marked('> ' + props.value, {renderer, breaks: true})
|
||||
|
||||
return (
|
||||
<div
|
||||
className='Quote'
|
||||
@@ -30,6 +31,7 @@ const Quote: ContentType = {
|
||||
useEffect(() => {
|
||||
ref.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<blockquote
|
||||
className='Quote'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import QuoteBlock from '.'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect} from 'react'
|
||||
import React, {useEffect, useRef} from 'react'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
import {Utils} from 'src/utils'
|
||||
@@ -16,6 +16,7 @@ const Text: ContentType = {
|
||||
editable: true,
|
||||
Display: (props: BlockInputProps) => {
|
||||
const html: string = Utils.htmlFromMarkdown(props.value || '')
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{__html: html}}
|
||||
@@ -28,6 +29,7 @@ const Text: ContentType = {
|
||||
useEffect(() => {
|
||||
ref.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
|
||||
@@ -18,6 +18,7 @@ const TextContent: ContentType = {
|
||||
editable: true,
|
||||
Display: (props: BlockInputProps) => {
|
||||
const html: string = Utils.htmlFromMarkdown(props.value || '')
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{__html: html}}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
import React from 'react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
import {render, act} from '@testing-library/react'
|
||||
import {act, render} from '@testing-library/react'
|
||||
|
||||
import {mockDOM, wrapDNDIntl, mockStateStore} from 'src/testUtils'
|
||||
import {mockDOM, mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
import TextBlock from '.'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useRef, useEffect, useState} from 'react'
|
||||
import React, {useEffect, useRef, useState} from 'react'
|
||||
|
||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||
import octoClient from 'src/octoClient'
|
||||
@@ -49,6 +49,7 @@ const Video: ContentType<FileInfo> = {
|
||||
</video>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
Input: (props: BlockInputProps<FileInfo>) => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import octoClient from 'src/octoClient'
|
||||
|
||||
@@ -4,21 +4,20 @@
|
||||
import React from 'react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
fireEvent,
|
||||
act
|
||||
} from '@testing-library/react'
|
||||
|
||||
import {
|
||||
mockDOM,
|
||||
wrapDNDIntl,
|
||||
mockStateStore,
|
||||
setup
|
||||
setup,
|
||||
wrapDNDIntl,
|
||||
} from 'src/testUtils'
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
|
||||
import {BlockData} from './blocks/types'
|
||||
import BlocksEditor from './blocksEditor'
|
||||
|
||||
@@ -120,7 +119,6 @@ describe('components/blocksEditor/blocksEditor', () => {
|
||||
await user.keyboard('{Enter}')
|
||||
})
|
||||
|
||||
|
||||
expect(onBlockCreated).toBeCalledWith(expect.objectContaining({value: 'test'}))
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useState, useMemo} from 'react'
|
||||
import React, {useMemo, useState} from 'react'
|
||||
import {DndProvider} from 'react-dnd'
|
||||
import {HTML5Backend} from 'react-dnd-html5-backend'
|
||||
|
||||
@@ -22,6 +22,7 @@ function BlocksEditor(props: Props) {
|
||||
const [editing, setEditing] = useState<BlockData|null>(null)
|
||||
const [afterBlock, setAfterBlock] = useState<BlockData|null>(null)
|
||||
const contentOrder = useMemo(() => props.blocks.filter((b) => b.id).map((b) => b.id!), [props.blocks])
|
||||
|
||||
return (
|
||||
<div
|
||||
className='BlocksEditor'
|
||||
@@ -34,6 +35,7 @@ function BlocksEditor(props: Props) {
|
||||
setEditing(afterBlock)
|
||||
}
|
||||
setAfterBlock(null)
|
||||
|
||||
return
|
||||
}
|
||||
let prevBlock = null
|
||||
@@ -97,6 +99,7 @@ function BlocksEditor(props: Props) {
|
||||
const newBlock = await props.onBlockModified(b)
|
||||
setNextType(registry.get(b.contentType).nextType || '')
|
||||
setAfterBlock(newBlock)
|
||||
|
||||
return newBlock
|
||||
}}
|
||||
onMove={props.onBlockMoved}
|
||||
@@ -109,6 +112,7 @@ function BlocksEditor(props: Props) {
|
||||
const newBlock = await props.onBlockCreated(b, afterBlock)
|
||||
setNextType(registry.get(b.contentType).nextType || '')
|
||||
setAfterBlock(newBlock)
|
||||
|
||||
return newBlock
|
||||
}}
|
||||
/>)}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
import React from 'react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
import {render, screen, act} from '@testing-library/react'
|
||||
import {act, render, screen} from '@testing-library/react'
|
||||
|
||||
import {
|
||||
mockDOM,
|
||||
wrapDNDIntl,
|
||||
mockStateStore,
|
||||
setup
|
||||
setup,
|
||||
wrapDNDIntl,
|
||||
} from 'src/testUtils'
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useState, useEffect} from 'react'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
|
||||
import * as contentBlocks from './blocks/'
|
||||
import {ContentType, BlockData} from './blocks/types'
|
||||
import {BlockData, ContentType} from './blocks/types'
|
||||
import RootInput from './rootInput'
|
||||
|
||||
import './editor.scss'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import RootInput from './rootInput'
|
||||
|
||||
|
||||
@@ -4,19 +4,18 @@
|
||||
import React from 'react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
act,
|
||||
fireEvent
|
||||
} from '@testing-library/react'
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import octoClient from 'src/octoClient'
|
||||
import {mockStateStore} from 'src/testUtils'
|
||||
import {mockStateStore, wrapIntl} from 'src/testUtils'
|
||||
import {createBoard} from 'src/blocks/board'
|
||||
import {wrapIntl} from 'src/testUtils'
|
||||
|
||||
import BoardSelector from './boardSelector'
|
||||
|
||||
@@ -97,7 +96,7 @@ describe('components/boardSelector', () => {
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("escape button should unmount the component", () => {
|
||||
it('escape button should unmount the component', () => {
|
||||
mockedOctoClient.searchLinkableBoards.mockResolvedValueOnce([])
|
||||
|
||||
const store = mockStateStore([], state)
|
||||
@@ -114,10 +113,10 @@ describe('components/boardSelector', () => {
|
||||
expect(store.dispatch).toHaveBeenCalledTimes(0)
|
||||
|
||||
fireEvent.keyDown(getByText(/Link boards/i), {
|
||||
key: "Escape",
|
||||
code: "Escape",
|
||||
key: 'Escape',
|
||||
code: 'Escape',
|
||||
keyCode: 27,
|
||||
charCode: 27
|
||||
charCode: 27,
|
||||
})
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledTimes(2)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useState, useMemo, useCallback} from 'react'
|
||||
import {IntlProvider, useIntl, FormattedMessage} from 'react-intl'
|
||||
import React, {useCallback, useMemo, useState} from 'react'
|
||||
import {FormattedMessage, IntlProvider, useIntl} from 'react-intl'
|
||||
import debounce from 'lodash/debounce'
|
||||
|
||||
import {SuiteWindow} from 'src/types/index'
|
||||
@@ -13,10 +13,10 @@ import {useWebsockets} from 'src/hooks/websockets'
|
||||
|
||||
import octoClient from 'src/octoClient'
|
||||
import mutator from 'src/mutator'
|
||||
import {getCurrentTeamId, getAllTeams, Team} from 'src/store/teams'
|
||||
import {createBoard, Board} from 'src/blocks/board'
|
||||
import {useAppSelector, useAppDispatch} from 'src/store/hooks'
|
||||
import {EmptySearch, EmptyResults} from 'src/components/searchDialog/searchDialog'
|
||||
import {Team, getAllTeams, getCurrentTeamId} from 'src/store/teams'
|
||||
import {Board, createBoard} from 'src/blocks/board'
|
||||
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
||||
import {EmptyResults, EmptySearch} from 'src/components/searchDialog/searchDialog'
|
||||
import ConfirmationDialog from 'src/components/confirmationDialogBox'
|
||||
import Dialog from 'src/components/dialog'
|
||||
import SearchIcon from 'src/widgets/icons/search'
|
||||
@@ -67,7 +67,7 @@ const BoardSelector = () => {
|
||||
let updated = false
|
||||
results.forEach((board, idx) => {
|
||||
for (const newBoard of boards) {
|
||||
if (newBoard.id == board.id) {
|
||||
if (newBoard.id === board.id) {
|
||||
newResults[idx] = newBoard
|
||||
updated = true
|
||||
}
|
||||
@@ -95,6 +95,7 @@ const BoardSelector = () => {
|
||||
const linkBoard = async (board: Board, confirmed?: boolean): Promise<void> => {
|
||||
if (!confirmed) {
|
||||
setShowLinkBoardConfirmation(board)
|
||||
|
||||
return
|
||||
}
|
||||
const newBoard = createBoard({...board, channelId: currentChannel})
|
||||
@@ -117,7 +118,7 @@ const BoardSelector = () => {
|
||||
}
|
||||
|
||||
let confirmationSubText
|
||||
if (showLinkBoardConfirmation?.channelId !== '') {
|
||||
if (showLinkBoardConfirmation?.channelId) {
|
||||
confirmationSubText = intl.formatMessage({
|
||||
id: 'boardSelector.confirm-link-board-subtext-with-other-channel',
|
||||
defaultMessage: 'When you link "{boardName}" to the channel, all members of the channel (existing and new) will be able to edit it. This excludes members who are guests.{lineBreak} This board is currently linked to another channel. It will be unlinked if you choose to link it here.',
|
||||
@@ -138,7 +139,7 @@ const BoardSelector = () => {
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key == 'Escape') {
|
||||
if (event.key === 'Escape') {
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
@@ -197,13 +198,15 @@ const BoardSelector = () => {
|
||||
<div className='searchResults'>
|
||||
{/*When there are results to show*/}
|
||||
{searchQuery && results.length > 0 &&
|
||||
results.map((result) => (<BoardSelectorItem
|
||||
key={result.id}
|
||||
item={result}
|
||||
linkBoard={linkBoard}
|
||||
unlinkBoard={unlinkBoard}
|
||||
currentChannel={currentChannel}
|
||||
/>))}
|
||||
results.map((result) => (
|
||||
<BoardSelectorItem
|
||||
key={result.id}
|
||||
item={result}
|
||||
linkBoard={linkBoard}
|
||||
unlinkBoard={unlinkBoard}
|
||||
currentChannel={currentChannel}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/*when user searched for something and there were no results*/}
|
||||
{emptyResult && <EmptyResults query={searchQuery}/>}
|
||||
|
||||
@@ -14,7 +14,7 @@ import BoardSelectorItem from './boardSelectorItem'
|
||||
describe('components/boardSelectorItem', () => {
|
||||
it('renders board without title', async () => {
|
||||
const board = createBoard()
|
||||
board.title = ""
|
||||
board.title = ''
|
||||
|
||||
const {container} = render(wrapIntl(
|
||||
<BoardSelectorItem
|
||||
@@ -29,7 +29,7 @@ describe('components/boardSelectorItem', () => {
|
||||
|
||||
it('renders linked board', async () => {
|
||||
const board = createBoard()
|
||||
board.title = "Test title"
|
||||
board.title = 'Test title'
|
||||
|
||||
const {container} = render(wrapIntl(
|
||||
<BoardSelectorItem
|
||||
@@ -44,7 +44,7 @@ describe('components/boardSelectorItem', () => {
|
||||
|
||||
it('renders not linked board', async () => {
|
||||
const board = createBoard()
|
||||
board.title = "Test title"
|
||||
board.title = 'Test title'
|
||||
|
||||
const {container} = render(wrapIntl(
|
||||
<BoardSelectorItem
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {useIntl, FormattedMessage} from 'react-intl'
|
||||
import {FormattedMessage, useIntl} from 'react-intl'
|
||||
|
||||
import {Board} from 'src/blocks/board'
|
||||
import Button from 'src/widgets/buttons/button'
|
||||
@@ -22,6 +22,7 @@ const BoardSelectorItem = (props: Props) => {
|
||||
const intl = useIntl()
|
||||
const untitledBoardTitle = intl.formatMessage({id: 'ViewTitle.untitled-board', defaultMessage: 'Untitled board'})
|
||||
const resultTitle = item.title || untitledBoardTitle
|
||||
|
||||
return (
|
||||
<div className='BoardSelectorItem'>
|
||||
<div className='BoardSelectorItem-info'>
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
within
|
||||
within,
|
||||
} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import React from 'react'
|
||||
@@ -20,7 +20,7 @@ import {MemoryRouter, Router} from 'react-router-dom'
|
||||
|
||||
import Mutator from 'src/mutator'
|
||||
import {Team} from 'src/store/teams'
|
||||
import {createBoard, Board} from 'src/blocks/board'
|
||||
import {Board, createBoard} from 'src/blocks/board'
|
||||
import {IUser} from 'src/user'
|
||||
import {mockDOM, mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||
|
||||
@@ -288,7 +288,7 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
||||
|
||||
const useTemplateButton = screen.getByText('Use this template').parentElement
|
||||
expect(useTemplateButton).not.toBeNull()
|
||||
|
||||
|
||||
await userEvent.click(useTemplateButton!)
|
||||
|
||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
||||
@@ -313,11 +313,10 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
||||
expect(divBoardToSelect).not.toBeNull()
|
||||
|
||||
await userEvent.click(divBoardToSelect!)
|
||||
|
||||
|
||||
const useTemplateButton = screen.getByText('Use this template').parentElement
|
||||
expect(useTemplateButton).not.toBeNull()
|
||||
|
||||
|
||||
await userEvent.click(useTemplateButton!)
|
||||
|
||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
||||
@@ -339,12 +338,12 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
||||
expect(divBoardToSelect).not.toBeNull()
|
||||
|
||||
await userEvent.click(divBoardToSelect!)
|
||||
|
||||
|
||||
const useTemplateButton = screen.getByText('Use this template').parentElement
|
||||
expect(useTemplateButton).not.toBeNull()
|
||||
|
||||
|
||||
await userEvent.click(useTemplateButton!)
|
||||
|
||||
|
||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledWith(team1.id, expect.anything(), expect.anything(), expect.anything(), 'global-1', team1.id))
|
||||
await waitFor(() => expect(mockedTelemetry.trackEvent).toBeCalledWith('boards', 'createBoardViaTemplate', {boardTemplateId: 'template_id_global'}))
|
||||
@@ -367,9 +366,9 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
||||
|
||||
const useTemplateButton = screen.getByText('Use this template').parentElement
|
||||
expect(useTemplateButton).not.toBeNull()
|
||||
|
||||
|
||||
await userEvent.click(useTemplateButton!)
|
||||
|
||||
|
||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledWith(team1.id, expect.anything(), expect.anything(), expect.anything(), '2', team1.id))
|
||||
await waitFor(() => expect(mockedTelemetry.trackEvent).toBeCalledWith('boards', 'createBoardViaTemplate', {boardTemplateId: 'template_id_2'}))
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {
|
||||
useEffect,
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {FormattedMessage, useIntl} from 'react-intl'
|
||||
import {useHistory, useRouteMatch} from 'react-router-dom'
|
||||
@@ -18,8 +18,8 @@ import CloseIcon from 'src/widgets/icons/close'
|
||||
import Button from 'src/widgets/buttons/button'
|
||||
import octoClient from 'src/octoClient'
|
||||
import mutator from 'src/mutator'
|
||||
import {getTemplates, getCurrentBoardId} from 'src/store/boards'
|
||||
import {getCurrentTeam, Team} from 'src/store/teams'
|
||||
import {getCurrentBoardId, getTemplates} from 'src/store/boards'
|
||||
import {Team, getCurrentTeam} from 'src/store/teams'
|
||||
import {fetchGlobalTemplates, getGlobalTemplates} from 'src/store/globalTemplates'
|
||||
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
||||
import TelemetryClient, {TelemetryActions, TelemetryCategory} from 'src/telemetry/telemetryClient'
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {
|
||||
render,
|
||||
within,
|
||||
act,
|
||||
waitFor
|
||||
render,
|
||||
waitFor,
|
||||
within,
|
||||
} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import React from 'react'
|
||||
import {MockStoreEnhanced} from 'redux-mock-store'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
import {Board, MemberRole, IPropertyTemplate} from 'src/blocks/board'
|
||||
import {Board, IPropertyTemplate, MemberRole} from 'src/blocks/board'
|
||||
import {mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||
|
||||
import {IUser} from 'src/user'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useEffect, useState, useMemo} from 'react'
|
||||
import React, {useEffect, useMemo, useState} from 'react'
|
||||
|
||||
import {Board} from 'src/blocks/board'
|
||||
import {Card} from 'src/blocks/card'
|
||||
@@ -45,6 +45,7 @@ const BoardTemplateSelectorPreview = (props: Props) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
isSubscribed = false
|
||||
}
|
||||
@@ -62,6 +63,7 @@ const BoardTemplateSelectorPreview = (props: Props) => {
|
||||
if (!activeView) {
|
||||
return {visible: [], hidden: []}
|
||||
}
|
||||
|
||||
return getVisibleAndHiddenGroups(activeTemplateCards, activeView.fields.visibleOptionIds, activeView?.fields.hiddenOptionIds, groupByProperty)
|
||||
}, [activeTemplateCards, activeView, groupByProperty])
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import AddIcon from 'src/widgets/icons/add'
|
||||
import BoardSwitcherDialog from 'src/components/boardsSwitcherDialog/boardSwitcherDialog'
|
||||
import {Utils} from 'src/utils'
|
||||
import {Constants} from 'src/constants'
|
||||
import {TOUR_SIDEBAR, SidebarTourSteps} from 'src/components/onboardingTour'
|
||||
import {SidebarTourSteps, TOUR_SIDEBAR} from 'src/components/onboardingTour'
|
||||
|
||||
import IconButton from 'src/widgets/buttons/iconButton'
|
||||
import SearchForBoardsTourStep from 'src/components/onboardingTour/searchForBoards/searchForBoards'
|
||||
|
||||
@@ -9,7 +9,7 @@ import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
import {render} from '@testing-library/react'
|
||||
|
||||
import {createMemoryHistory, History} from 'history'
|
||||
import {History, createMemoryHistory} from 'history'
|
||||
|
||||
import {Router} from 'react-router-dom'
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {
|
||||
MutableRefObject,
|
||||
ReactNode,
|
||||
useRef,
|
||||
createRef,
|
||||
useState,
|
||||
useEffect,
|
||||
MutableRefObject
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
import './boardSwitcherDialog.scss'
|
||||
@@ -19,7 +19,7 @@ import SearchDialog from 'src/components/searchDialog/searchDialog'
|
||||
import Globe from 'src/widgets/icons/globe'
|
||||
import LockOutline from 'src/widgets/icons/lockOutline'
|
||||
import {useAppSelector} from 'src/store/hooks'
|
||||
import {getAllTeams, getCurrentTeam, Team} from 'src/store/teams'
|
||||
import {Team, getAllTeams, getCurrentTeam} from 'src/store/teams'
|
||||
import {getMe} from 'src/store/users'
|
||||
import {Utils} from 'src/utils'
|
||||
import {BoardTypeOpen, BoardTypePrivate} from 'src/blocks/board'
|
||||
@@ -75,6 +75,7 @@ const BoardSwitcherDialog = (props: Props): JSX.Element => {
|
||||
const untitledBoardTitle = intl.formatMessage({id: 'ViewTitle.untitled-board', defaultMessage: 'Untitled board'})
|
||||
refs.current = items.map((_, i) => refs.current[i] ?? createRef())
|
||||
setRefs(refs)
|
||||
|
||||
return items.map((item, i) => {
|
||||
const resultTitle = item.title || untitledBoardTitle
|
||||
const teamTitle = teamsById[item.teamId].title
|
||||
@@ -83,6 +84,7 @@ const BoardSwitcherDialog = (props: Props): JSX.Element => {
|
||||
...prevIDs,
|
||||
[i]: [item.teamId, item.id],
|
||||
}))
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.id}
|
||||
|
||||
@@ -130,6 +130,7 @@ describe('components/boardsUnfurl/BoardsUnfurl', () => {
|
||||
})
|
||||
|
||||
const board = {...createBoard(), title: 'test board'}
|
||||
|
||||
// mockedOctoClient.getBoard.mockResolvedValueOnce(board)
|
||||
|
||||
const component = (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useState, useEffect} from 'react'
|
||||
import {IntlProvider, FormattedMessage, useIntl} from 'react-intl'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {FormattedMessage, IntlProvider, useIntl} from 'react-intl'
|
||||
|
||||
import WithWebSockets from 'src/components/withWebSockets'
|
||||
import {useWebsockets} from 'src/hooks/websockets'
|
||||
@@ -10,7 +10,7 @@ import {getLanguage} from 'src/store/language'
|
||||
import {useAppSelector} from 'src/store/hooks'
|
||||
import {getCurrentTeamId} from 'src/store/teams'
|
||||
|
||||
import {WSClient, MMWebSocketClient} from 'src/wsclient'
|
||||
import {MMWebSocketClient, WSClient} from 'src/wsclient'
|
||||
import manifest from 'src/manifest'
|
||||
|
||||
import {getMessages} from 'src/i18n'
|
||||
@@ -86,6 +86,7 @@ export const BoardsUnfurl = (props: Props): JSX.Element => {
|
||||
const [firstCard] = cards as Card[]
|
||||
if (!firstCard || !fetchedBoard || firstCard.type !== 'card') {
|
||||
setLoading(false)
|
||||
|
||||
return null
|
||||
}
|
||||
setCard(firstCard)
|
||||
@@ -102,12 +103,14 @@ export const BoardsUnfurl = (props: Props): JSX.Element => {
|
||||
const [firstContentBlock] = contentBlock
|
||||
if (!firstContentBlock) {
|
||||
setLoading(false)
|
||||
|
||||
return null
|
||||
}
|
||||
setContent(firstContentBlock)
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
|
||||
return null
|
||||
}
|
||||
fetchData()
|
||||
|
||||
@@ -44,8 +44,8 @@ function fixTimestampToMinutesAccuracy(timestamp: number) {
|
||||
}
|
||||
|
||||
function cardsWithValue(cards: readonly Card[], property: IPropertyTemplate): Card[] {
|
||||
return cards.
|
||||
filter((card) => Boolean(getCardProperty(card, property)))
|
||||
return cards
|
||||
.filter((card) => Boolean(getCardProperty(card, property)))
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
@@ -66,6 +66,7 @@ function percentEmpty(cards: readonly Card[], property: IPropertyTemplate): stri
|
||||
if (cards.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return String((((cards.length - cardsWithValue(cards, property).length) / cards.length) * 100).toFixed(0)) + '%'
|
||||
}
|
||||
|
||||
@@ -73,6 +74,7 @@ function percentNotEmpty(cards: readonly Card[], property: IPropertyTemplate): s
|
||||
if (cards.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return String(((cardsWithValue(cards, property).length / cards.length) * 100).toFixed(0)) + '%'
|
||||
}
|
||||
|
||||
@@ -80,8 +82,8 @@ function countValueHelper(cards: readonly Card[], property: IPropertyTemplate):
|
||||
let values = 0
|
||||
|
||||
if (property.type === 'multiSelect') {
|
||||
cardsWithValue(cards, property).
|
||||
forEach((card) => {
|
||||
cardsWithValue(cards, property)
|
||||
.forEach((card) => {
|
||||
values += (getCardProperty(card, property) as string[]).length
|
||||
})
|
||||
} else {
|
||||
@@ -140,8 +142,8 @@ function countUniqueValue(cards: readonly Card[], property: IPropertyTemplate):
|
||||
function sum(cards: readonly Card[], property: IPropertyTemplate): string {
|
||||
let result = 0
|
||||
|
||||
cardsWithValue(cards, property).
|
||||
forEach((card) => {
|
||||
cardsWithValue(cards, property)
|
||||
.forEach((card) => {
|
||||
result += parseFloat(getCardProperty(card, property) as string)
|
||||
})
|
||||
|
||||
@@ -156,12 +158,13 @@ function average(cards: readonly Card[], property: IPropertyTemplate): string {
|
||||
|
||||
const result = parseFloat(sum(cards, property))
|
||||
const avg = result / numCards
|
||||
|
||||
return String(Utils.roundTo(avg, ROUNDED_DECIMAL_PLACES))
|
||||
}
|
||||
|
||||
function median(cards: readonly Card[], property: IPropertyTemplate): string {
|
||||
const sorted = cardsWithValue(cards, property).
|
||||
sort((a, b) => {
|
||||
const sorted = cardsWithValue(cards, property)
|
||||
.sort((a, b) => {
|
||||
if (!getCardProperty(a, property)) {
|
||||
return 1
|
||||
}
|
||||
@@ -231,6 +234,7 @@ function earliest(cards: readonly Card[], property: IPropertyTemplate, intl: Int
|
||||
return ''
|
||||
}
|
||||
const date = new Date(result)
|
||||
|
||||
return property.type === 'date' ? Utils.displayDate(date, intl) : Utils.displayDateTime(date, intl)
|
||||
}
|
||||
|
||||
@@ -242,6 +246,7 @@ function earliestEpoch(cards: readonly Card[], property: IPropertyTemplate): num
|
||||
result = Math.min(result, timestamp)
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -251,6 +256,7 @@ function latest(cards: readonly Card[], property: IPropertyTemplate, intl: IntlS
|
||||
return ''
|
||||
}
|
||||
const date = new Date(result)
|
||||
|
||||
return property.type === 'date' ? Utils.displayDate(date, intl) : Utils.displayDateTime(date, intl)
|
||||
}
|
||||
|
||||
@@ -262,6 +268,7 @@ function latestEpoch(cards: readonly Card[], property: IPropertyTemplate): numbe
|
||||
result = Math.max(result, timestamp)
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -276,10 +283,12 @@ function getTimestampsFromPropertyValue(value: number | string | string[]): numb
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
return [property.from, property.to].flatMap((e) => {
|
||||
return e ? [e] : []
|
||||
})
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -292,6 +301,7 @@ function dateRange(cards: readonly Card[], property: IPropertyTemplate, intl: In
|
||||
if (resultLatest === Number.NEGATIVE_INFINITY) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return moment.duration(resultLatest - resultEarliest, 'milliseconds').locale(intl.locale.toLowerCase()).humanize()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
|
||||
import Select, {components, DropdownIndicatorProps, StylesConfig} from 'react-select'
|
||||
import Select, {DropdownIndicatorProps, StylesConfig, components} from 'react-select'
|
||||
|
||||
|
||||
import {useIntl, IntlShape} from 'react-intl'
|
||||
import {IntlShape, useIntl} from 'react-intl'
|
||||
|
||||
import {getSelectBaseStyle} from 'src/theme'
|
||||
import ChevronUp from 'src/widgets/icons/chevronUp'
|
||||
|
||||
@@ -5,7 +5,7 @@ import {render} from '@testing-library/react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
import {wrapIntl, mockStateStore} from 'src/testUtils'
|
||||
import {mockStateStore, wrapIntl} from 'src/testUtils'
|
||||
import {IPropertyTemplate} from 'src/blocks/board'
|
||||
|
||||
import CalendarView from './fullCalendar'
|
||||
|
||||
@@ -5,10 +5,10 @@ import React, {useCallback, useMemo, useState} from 'react'
|
||||
import {useIntl} from 'react-intl'
|
||||
|
||||
import FullCalendar, {
|
||||
DayCellContentArg,
|
||||
EventChangeArg,
|
||||
EventInput,
|
||||
EventContentArg,
|
||||
DayCellContentArg
|
||||
EventInput,
|
||||
} from '@fullcalendar/react'
|
||||
|
||||
import interactionPlugin from '@fullcalendar/interaction'
|
||||
@@ -60,6 +60,7 @@ function createDatePropertyFromCalendarDates(start: Date, end: Date): DateProper
|
||||
if (dateTo !== dateFrom) {
|
||||
dateProperty.to = dateTo
|
||||
}
|
||||
|
||||
return dateProperty
|
||||
}
|
||||
|
||||
@@ -69,6 +70,7 @@ function createDatePropertyFromCalendarDate(start: Date): DateProperty {
|
||||
const dateFrom = start.getTime() - timeZoneOffset(start.getTime())
|
||||
|
||||
const dateProperty: DateProperty = {from: dateFrom}
|
||||
|
||||
return dateProperty
|
||||
}
|
||||
|
||||
@@ -97,6 +99,7 @@ const CalendarFullView = (props: Props): JSX.Element|null => {
|
||||
if (readonly || !dateDisplayProperty || propsRegistry.get(dateDisplayProperty.type).isReadOnly) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, [readonly, dateDisplayProperty])
|
||||
|
||||
@@ -118,6 +121,7 @@ const CalendarFullView = (props: Props): JSX.Element|null => {
|
||||
//full calendar end date is exclusive, so increment by 1 day.
|
||||
dateTo.setDate(dateTo.getDate() + 1)
|
||||
}
|
||||
|
||||
return [{
|
||||
id: card.id,
|
||||
title: card.title,
|
||||
|
||||
@@ -64,6 +64,7 @@ const calculateBadges = (contents: ContentsType, comments: CommentBlock[]): Badg
|
||||
updateCounters(content)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
description: text > 0,
|
||||
comments: comments.length,
|
||||
@@ -84,6 +85,7 @@ const CardBadges = (props: Props) => {
|
||||
}
|
||||
const intl = useIntl()
|
||||
const {checkboxes} = badges
|
||||
|
||||
return (
|
||||
<div className={`CardBadges ${className || ''}`}>
|
||||
{badges.description &&
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {
|
||||
Fragment,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
Fragment,
|
||||
useMemo
|
||||
} from 'react'
|
||||
import {FormattedMessage, useIntl, IntlShape} from 'react-intl'
|
||||
import {FormattedMessage, IntlShape, useIntl} from 'react-intl'
|
||||
|
||||
import {BlockIcons} from 'src/blockIcons'
|
||||
import {Card} from 'src/blocks/card'
|
||||
@@ -30,7 +30,7 @@ import TelemetryClient, {TelemetryActions, TelemetryCategory} from 'src/telemetr
|
||||
import BlockIconSelector from 'src/components/blockIconSelector'
|
||||
|
||||
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
||||
import {updateCards, setCurrent as setCurrentCard} from 'src/store/cards'
|
||||
import {setCurrent as setCurrentCard, updateCards} from 'src/store/cards'
|
||||
import {updateContents} from 'src/store/contents'
|
||||
import {Permission} from 'src/constants'
|
||||
import {useHasCurrentBoardPermissions} from 'src/hooks/permissions'
|
||||
@@ -102,6 +102,7 @@ async function addBlockNewEditor(card: Card, intl: IntlShape, title: string, fie
|
||||
|
||||
const newBlock = await mutator.insertBlock(block.boardId, block, description, afterRedo, beforeUndo)
|
||||
dispatch(updateContents([newBlock]))
|
||||
|
||||
return newBlock
|
||||
}
|
||||
|
||||
@@ -144,7 +145,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
saveTitleRef.current && saveTitleRef.current()
|
||||
saveTitleRef.current?.()
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -339,6 +340,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
||||
} else {
|
||||
newBlock = await addBlockNewEditor(card, intl, block.value, {}, block.contentType, afterBlock?.id, dispatch)
|
||||
}
|
||||
|
||||
return {...block, id: newBlock.id}
|
||||
}}
|
||||
onBlockModified={async (block: any): Promise<BlockData<any>|null> => {
|
||||
@@ -351,6 +353,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
||||
const description = intl.formatMessage({id: 'ContentBlock.DeleteAction', defaultMessage: 'delete'})
|
||||
|
||||
mutator.deleteBlock(originalContentBlock, description)
|
||||
|
||||
return null
|
||||
}
|
||||
const newBlock = {
|
||||
@@ -363,6 +366,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
||||
newBlock.fields = {...newBlock.fields, value: block.value.checked}
|
||||
}
|
||||
mutator.updateBlock(card.boardId, newBlock, originalContentBlock, intl.formatMessage({id: 'ContentBlock.editCardText', defaultMessage: 'edit card text'}))
|
||||
|
||||
return block
|
||||
}}
|
||||
onBlockMoved={async (block: BlockData, beforeBlock: BlockData|null, afterBlock: BlockData|null): Promise<void> => {
|
||||
@@ -372,6 +376,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
||||
let sourceWhere: 'after'|'before'
|
||||
if (idx === -1) {
|
||||
Utils.logError('Unable to find the block id in the order of the current block')
|
||||
|
||||
return
|
||||
}
|
||||
if (idx === 0) {
|
||||
@@ -383,6 +388,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
||||
}
|
||||
if (afterBlock && afterBlock.id) {
|
||||
await mutator.moveContentBlock(block.id, afterBlock.id, 'after', sourceBlockId, sourceWhere, intl.formatMessage({id: 'ContentBlock.moveBlock', defaultMessage: 'move card content'}))
|
||||
|
||||
return
|
||||
}
|
||||
if (beforeBlock && beforeBlock.id) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {act} from 'react-dom/test-utils'
|
||||
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
import {mockDOM, wrapDNDIntl, mockStateStore} from 'src/testUtils'
|
||||
import {mockDOM, mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||
|
||||
import CardDetailContents from './cardDetailContents'
|
||||
import {CardDetailProvider} from './cardDetailContext'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
import {useIntl, IntlShape} from 'react-intl'
|
||||
import {IntlShape, useIntl} from 'react-intl'
|
||||
|
||||
import {IContentBlockWithCords, ContentBlock as ContentBlockType} from 'src/blocks/contentBlock'
|
||||
import {ContentBlock as ContentBlockType, IContentBlockWithCords} from 'src/blocks/contentBlock'
|
||||
import {Card} from 'src/blocks/card'
|
||||
import {createTextBlock} from 'src/blocks/textBlock'
|
||||
import {Block} from 'src/blocks/block'
|
||||
@@ -182,6 +182,7 @@ const CardDetailContents = (props: Props) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='octo-content CardDetailContents'>
|
||||
<div className='octo-block'>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import {wrapIntl, mockStateStore, setup} from 'src/testUtils'
|
||||
import {mockStateStore, setup, wrapIntl} from 'src/testUtils'
|
||||
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ function addContentMenu(intl: IntlShape, type: BlockTypes): JSX.Element {
|
||||
const handler = contentRegistry.getHandler(type)
|
||||
if (!handler) {
|
||||
Utils.logError(`addContentMenu, unknown content type: ${type}`)
|
||||
|
||||
return <></>
|
||||
}
|
||||
const cardDetail = useCardDetailContext()
|
||||
@@ -39,6 +40,7 @@ function addContentMenu(intl: IntlShape, type: BlockTypes): JSX.Element {
|
||||
|
||||
const CardDetailContentsMenu = () => {
|
||||
const intl = useIntl()
|
||||
|
||||
return (
|
||||
<div className='CardDetailContentsMenu content add-content'>
|
||||
<MenuWrapper>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {
|
||||
createContext,
|
||||
ReactElement,
|
||||
ReactNode,
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
useCallback
|
||||
} from 'react'
|
||||
|
||||
import {useIntl} from 'react-intl'
|
||||
@@ -37,6 +37,7 @@ export function useCardDetailContext(): CardDetailContextType {
|
||||
if (!cardDetailContext) {
|
||||
throw new Error('CardDetailContext is not available!')
|
||||
}
|
||||
|
||||
return cardDetailContext
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
import React from 'react'
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
act,
|
||||
fireEvent
|
||||
} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import {mocked} from 'jest-mock'
|
||||
@@ -206,7 +206,7 @@ describe('components/cardDetail/CardDetailProperties', () => {
|
||||
await act(() => userEvent.click(menuElement))
|
||||
const numberType = screen.getByRole('button', {name: /number/i})
|
||||
|
||||
await act( () => userEvent.click(numberType))
|
||||
await act(() => userEvent.click(numberType))
|
||||
|
||||
expect(mockedMutator.insertPropertyTemplate).toHaveBeenCalledTimes(1)
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ const CardDetailProperties = (props: Props) => {
|
||||
// if only the name has changed, set the property without warning
|
||||
if (affectsNumOfCards === '0' || oldType === newType) {
|
||||
mutator.changePropertyTypeAndName(board, cards, propertyTemplate, newType.type, newName)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import moment from 'moment'
|
||||
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import {wrapIntl, mockStateStore} from 'src/testUtils'
|
||||
import {mockStateStore, wrapIntl} from 'src/testUtils'
|
||||
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ const CommentsList = (props: Props) => {
|
||||
// Only modify _own_ comments, EXCEPT for Admins, which can delete _any_ comment
|
||||
// NOTE: editing comments will exist in the future (in addition to deleting)
|
||||
const canDeleteComment: boolean = canDeleteOthersComments || me?.id === comment.modifiedBy
|
||||
|
||||
return (
|
||||
<Comment
|
||||
key={comment.id}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {useEffect, useCallback} from 'react'
|
||||
import {useCallback, useEffect} from 'react'
|
||||
import {useIntl} from 'react-intl'
|
||||
|
||||
import {ImageBlock, createImageBlock} from 'src/blocks/imageBlock'
|
||||
@@ -77,6 +77,7 @@ export default function useImagePaste(boardId: string, cardId: string, contentOr
|
||||
useEffect(() => {
|
||||
document.addEventListener('paste', onPaste)
|
||||
document.addEventListener('drop', onDrop)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('paste', onPaste)
|
||||
document.removeEventListener('drop', onDrop)
|
||||
|
||||
@@ -93,7 +93,6 @@ describe('components/cardDialog', () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
test('should match snapshot', async () => {
|
||||
|
||||
const {container} = render(wrapDNDIntl(
|
||||
<ReduxProvider store={store}>
|
||||
<CardDialog
|
||||
@@ -324,7 +323,7 @@ describe('components/cardDialog', () => {
|
||||
/>
|
||||
</ReduxProvider>,
|
||||
))
|
||||
|
||||
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useState, useCallback} from 'react'
|
||||
import React, {useCallback, useState} from 'react'
|
||||
import {FormattedMessage, useIntl} from 'react-intl'
|
||||
|
||||
import {Board} from 'src/blocks/board'
|
||||
@@ -67,6 +67,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
||||
const makeTemplateClicked = async () => {
|
||||
if (!card) {
|
||||
Utils.assertFailure('card')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -89,6 +90,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
||||
const handleDeleteCard = async () => {
|
||||
if (!card) {
|
||||
Utils.assertFailure()
|
||||
|
||||
return
|
||||
}
|
||||
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.DeleteCard, {board: props.board.id, view: props.activeView.id, card: card.id})
|
||||
@@ -111,6 +113,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
||||
// so adding des
|
||||
if (card?.title === '' && card?.fields.contentOrder.length === 0) {
|
||||
handleDeleteCard()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -266,6 +269,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
||||
if (!isTemplate && !card?.limited) {
|
||||
return (<>{attachBtn()}{following ? unfollowBtn : followBtn}</>)
|
||||
}
|
||||
|
||||
return (<>{attachBtn()}</>)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useCallback, useEffect, useState} from 'react'
|
||||
import {useIntl, FormattedMessage} from 'react-intl'
|
||||
import {FormattedMessage, useIntl} from 'react-intl'
|
||||
|
||||
import AlertIcon from 'src/widgets/icons/alert'
|
||||
|
||||
import {useAppSelector, useAppDispatch} from 'src/store/hooks'
|
||||
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
||||
import {IUser, UserConfigPatch} from 'src/user'
|
||||
import {
|
||||
getCardHiddenWarningSnoozeUntil,
|
||||
getCardLimitSnoozeUntil,
|
||||
getMe,
|
||||
patchProps,
|
||||
getCardLimitSnoozeUntil,
|
||||
getCardHiddenWarningSnoozeUntil
|
||||
} from 'src/store/users'
|
||||
import {getCurrentBoardHiddenCardsCount, getCardHiddenWarning} from 'src/store/cards'
|
||||
import {getCardHiddenWarning, getCurrentBoardHiddenCardsCount} from 'src/store/cards'
|
||||
import TelemetryClient, {TelemetryActions, TelemetryCategory} from 'src/telemetry/telemetryClient'
|
||||
import CheckIcon from 'src/widgets/icons/check'
|
||||
import NotificationBox from 'src/widgets/notificationBox/notificationBox'
|
||||
@@ -103,10 +103,12 @@ const CardLimitNotification = (props: Props) => {
|
||||
useEffect(() => {
|
||||
if (!show) {
|
||||
const interval = setInterval(() => setTime(Date.now()), checkSnoozeInterval)
|
||||
|
||||
return () => {
|
||||
clearInterval(interval)
|
||||
}
|
||||
}
|
||||
|
||||
return () => null
|
||||
}, [show])
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
within
|
||||
within,
|
||||
} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import React from 'react'
|
||||
@@ -375,7 +375,7 @@ describe('components/centerPanel', () => {
|
||||
))
|
||||
const cardElement = screen.getByRole('textbox', {name: 'card1'})
|
||||
expect(cardElement).not.toBeNull()
|
||||
fireEvent.click(cardElement, {shiftKey: true})
|
||||
fireEvent.click(cardElement, {shiftKey: true})
|
||||
expect(container).toMatchSnapshot()
|
||||
|
||||
//delete
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
/* eslint-disable max-lines */
|
||||
|
||||
import React, {
|
||||
useState,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {useIntl} from 'react-intl'
|
||||
import {useHotkeys} from 'react-hotkeys-hook'
|
||||
@@ -15,32 +15,32 @@ import {ClientConfig} from 'src/config/clientConfig'
|
||||
import {Block} from 'src/blocks/block'
|
||||
import {BlockIcons} from 'src/blockIcons'
|
||||
import {Card, createCard} from 'src/blocks/card'
|
||||
import {Board, IPropertyTemplate, BoardGroup} from 'src/blocks/board'
|
||||
import {Board, BoardGroup, IPropertyTemplate} from 'src/blocks/board'
|
||||
import {BoardView} from 'src/blocks/boardView'
|
||||
import {CardFilter} from 'src/cardFilter'
|
||||
import mutator from 'src/mutator'
|
||||
import {Utils} from 'src/utils'
|
||||
import {UserSettings} from 'src/userSettings'
|
||||
import {
|
||||
getCurrentCard,
|
||||
addCard as addCardAction,
|
||||
addTemplate as addTemplateAction,
|
||||
showCardHiddenWarning
|
||||
getCurrentCard,
|
||||
showCardHiddenWarning,
|
||||
} from 'src/store/cards'
|
||||
import {getCardLimitTimestamp} from 'src/store/limits'
|
||||
import {updateView} from 'src/store/views'
|
||||
import {getVisibleAndHiddenGroups} from 'src/boardUtils'
|
||||
import TelemetryClient, {TelemetryCategory, TelemetryActions} from 'src/telemetry/telemetryClient'
|
||||
import TelemetryClient, {TelemetryActions, TelemetryCategory} from 'src/telemetry/telemetryClient'
|
||||
|
||||
import {getClientConfig} from 'src/store/clientConfig'
|
||||
|
||||
import './centerPanel.scss'
|
||||
|
||||
import {useAppSelector, useAppDispatch} from 'src/store/hooks'
|
||||
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
||||
|
||||
import {
|
||||
getMe,
|
||||
getBoardUsers,
|
||||
getMe,
|
||||
getOnboardingTourCategory,
|
||||
getOnboardingTourStarted,
|
||||
getOnboardingTourStep,
|
||||
@@ -72,7 +72,7 @@ import {
|
||||
BoardTourSteps,
|
||||
FINISHED,
|
||||
TOUR_BOARD,
|
||||
TOUR_CARD
|
||||
TOUR_CARD,
|
||||
} from './onboardingTour'
|
||||
import ShareBoardTourStep from './onboardingTour/shareBoard/shareBoard'
|
||||
|
||||
@@ -393,6 +393,7 @@ const CenterPanel = (props: Props) => {
|
||||
defaultMessage: 'No {propertyName}',
|
||||
}, {propertyName: groupByProperty?.name})
|
||||
}
|
||||
|
||||
return intl.formatMessage({id: 'centerPanel.unknown-user', defaultMessage: 'Unknown user'})
|
||||
}
|
||||
|
||||
@@ -408,6 +409,7 @@ const CenterPanel = (props: Props) => {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {visible: vg, hidden: hg}
|
||||
}, [cards, activeView.fields.visibleOptionIds, activeView.fields.hiddenOptionIds, groupByProperty, boardUsers])
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useState, useRef} from 'react'
|
||||
import React, {useRef, useState} from 'react'
|
||||
import Select from 'react-select'
|
||||
import {useIntl, FormattedMessage} from 'react-intl'
|
||||
import {FormattedMessage, useIntl} from 'react-intl'
|
||||
|
||||
import {MemberRole} from 'src/blocks/board'
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ const ArchivedFile = (props: Props): JSX.Element => {
|
||||
const fileExtension = useCallback(() => {
|
||||
let extension = props.fileInfo.extension
|
||||
extension = extension?.startsWith('.') ? extension?.substring(1) : extension
|
||||
|
||||
return extension?.toUpperCase()
|
||||
}, [props.fileInfo.extension])
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ const AttachmentElement = (props: Props): JSX.Element|null => {
|
||||
name: block.title,
|
||||
extension: block.title.split('.').slice(0, -1).join('.'),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
const attachmentInfo = await octoClient.getFileInfo(block.boardId, block.fields.fileId)
|
||||
@@ -65,8 +66,10 @@ const AttachmentElement = (props: Props): JSX.Element|null => {
|
||||
if (fName.length > 18) {
|
||||
let result = fName.slice(0, 15)
|
||||
result += '...'
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return fName
|
||||
}
|
||||
setFileName(generateFileName(fileInfo.name))
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
waitFor
|
||||
waitFor,
|
||||
} from '@testing-library/react'
|
||||
import {mocked} from 'jest-mock'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
@@ -174,7 +174,7 @@ describe('components/content/checkboxElement', () => {
|
||||
))
|
||||
const input = screen.getByRole('textbox')
|
||||
|
||||
// should delete if title is empty
|
||||
// should delete if title is empty
|
||||
await userEvent.type(input, '{Escape}')
|
||||
expect(deleteElement).toHaveBeenCalledTimes(1)
|
||||
await userEvent.type(input, '{Enter}')
|
||||
|
||||
@@ -68,6 +68,7 @@ const CheckboxElement = (props: Props) => {
|
||||
const {lastAddedBlock} = cardDetail
|
||||
if (title === '' && block.id === lastAddedBlock.id && lastAddedBlock.autoAdded && props.onDeleteElement) {
|
||||
props.onDeleteElement()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -77,6 +78,7 @@ const CheckboxElement = (props: Props) => {
|
||||
// Wait for the change to happen
|
||||
setTimeout(props.onAddElement, 100)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {ReactElement, ReactNode} from 'react'
|
||||
|
||||
|
||||
import {render} from '@testing-library/react'
|
||||
|
||||
import {wrapIntl} from 'src/testUtils'
|
||||
@@ -53,7 +52,7 @@ describe('components/content/contentElement', () => {
|
||||
})
|
||||
|
||||
it('should return null for unknown type', () => {
|
||||
jest.spyOn(console, 'error').mockImplementation()
|
||||
jest.spyOn(console, 'error').mockImplementation()
|
||||
|
||||
const block: ContentBlock = {...contentBlock, type: 'unknown'}
|
||||
const {container} = render(wrap(
|
||||
|
||||
@@ -30,6 +30,7 @@ export default function ContentElement(props: Props): JSX.Element|null {
|
||||
const handler = contentRegistry.getHandler(block.type)
|
||||
if (!handler) {
|
||||
Utils.logError(`ContentElement, unknown content type: ${block.type}`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
/* eslint-disable react/require-optimization */
|
||||
|
||||
import {IntlShape} from 'react-intl'
|
||||
|
||||
import {BlockTypes} from 'src/blocks/block'
|
||||
@@ -25,6 +25,7 @@ class ContentRegistry {
|
||||
registerContentType(entry: ContentHandler) {
|
||||
if (this.isContentType(entry.type)) {
|
||||
Utils.logError(`registerContentType, already registered type: ${entry.type}`)
|
||||
|
||||
return
|
||||
}
|
||||
this.registry.set(entry.type, entry)
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, act} from '@testing-library/react'
|
||||
import {act, render} from '@testing-library/react'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import {TextBlock} from 'src/blocks/textBlock'
|
||||
|
||||
import {mockDOM, wrapDNDIntl, mockStateStore} from 'src/testUtils'
|
||||
import {mockDOM, mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||
|
||||
import {Utils} from 'src/utils'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import {act, render, screen} from '@testing-library/react'
|
||||
|
||||
import React, {ReactNode, ReactElement} from 'react'
|
||||
import React, {ReactElement, ReactNode} from 'react'
|
||||
import {mocked} from 'jest-mock'
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
@@ -11,7 +11,7 @@ import userEvent from '@testing-library/user-event'
|
||||
|
||||
import {Utils} from 'src/utils'
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
import {mockDOM, wrapDNDIntl, mockStateStore} from 'src/testUtils'
|
||||
import {mockDOM, mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||
|
||||
import mutator from 'src/mutator'
|
||||
|
||||
|
||||
@@ -6,13 +6,12 @@ import {act, render, screen} from '@testing-library/react'
|
||||
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import {mockStateStore} from 'src/testUtils'
|
||||
import {wrapIntl} from 'src/testUtils'
|
||||
import {mockStateStore, wrapIntl} from 'src/testUtils'
|
||||
|
||||
import CreateBoardFromTemplate from './createBoardFromTemplate'
|
||||
|
||||
jest.mock('src/hooks/useGetAllTemplates', () => ({
|
||||
useGetAllTemplates: () => [{id: 'id', title: 'title', description: 'description', icon: '🍔'}]
|
||||
useGetAllTemplates: () => [{id: 'id', title: 'title', description: 'description', icon: '🍔'}],
|
||||
}))
|
||||
|
||||
describe('components/createBoardFromTemplate', () => {
|
||||
@@ -26,7 +25,7 @@ describe('components/createBoardFromTemplate', () => {
|
||||
const store = mockStateStore([], state)
|
||||
const setCanCreate = jest.fn
|
||||
const setAction = jest.fn
|
||||
const newBoardInfoIcon = (<i className="icon-information-outline" />)
|
||||
const newBoardInfoIcon = (<i className='icon-information-outline'/>)
|
||||
|
||||
const {container} = render(wrapIntl(
|
||||
<ReduxProvider store={store}>
|
||||
@@ -41,11 +40,11 @@ describe('components/createBoardFromTemplate', () => {
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('clicking checkbox toggles the templates selector', async () => {
|
||||
it.only('clicking checkbox toggles the templates selector', async () => {
|
||||
const store = mockStateStore([], state)
|
||||
const setCanCreate = jest.fn
|
||||
const setAction = jest.fn
|
||||
const newBoardInfoIcon = (<i className="icon-information-outline" />)
|
||||
const newBoardInfoIcon = (<i className='icon-information-outline'/>)
|
||||
|
||||
render(wrapIntl(
|
||||
<ReduxProvider store={store}>
|
||||
|
||||
@@ -4,19 +4,19 @@ import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
import {createIntl, createIntlCache, IntlProvider} from 'react-intl'
|
||||
import {IntlProvider, createIntl, createIntlCache} from 'react-intl'
|
||||
|
||||
import Select from 'react-select/async'
|
||||
import {
|
||||
components,
|
||||
FormatOptionLabelMeta,
|
||||
GroupBase,
|
||||
PlaceholderProps
|
||||
PlaceholderProps,
|
||||
SingleValue,
|
||||
components,
|
||||
} from 'react-select'
|
||||
import {SingleValue} from 'react-select'
|
||||
|
||||
import {CSSObject} from '@emotion/serialize'
|
||||
|
||||
@@ -49,7 +49,7 @@ const TEMPLATE_DESCRIPTION_LENGTH = 70
|
||||
const cache = createIntlCache()
|
||||
const intl = createIntl({
|
||||
locale: getCurrentLanguage(),
|
||||
messages: getMessages(getCurrentLanguage())
|
||||
messages: getMessages(getCurrentLanguage()),
|
||||
}, cache)
|
||||
|
||||
const {ValueContainer, Placeholder} = components
|
||||
@@ -60,7 +60,7 @@ const CustomValueContainer = ({children, ...props}: any) => {
|
||||
{props.selectProps.placeholder}
|
||||
</Placeholder>
|
||||
{React.Children.map(children, (child) =>
|
||||
child && child.type !== Placeholder ? child : null
|
||||
(child && child.type !== Placeholder ? child : null)
|
||||
)}
|
||||
</ValueContainer>
|
||||
)
|
||||
@@ -78,7 +78,6 @@ const CreateBoardFromTemplate = (props: Props) => {
|
||||
const templateIdRef = useRef('')
|
||||
templateIdRef.current = selectedBoardTemplateId
|
||||
|
||||
|
||||
const showNewBoardTemplateSelector = async () => {
|
||||
setAddBoard((prev: boolean) => !prev)
|
||||
}
|
||||
@@ -86,14 +85,14 @@ const CreateBoardFromTemplate = (props: Props) => {
|
||||
// CreateBoardFromTemplate
|
||||
const addBoardToChannel = async (channelId: string, teamId: string) => {
|
||||
if (!addBoardRef.current || !templateIdRef.current) {
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
|
||||
const ACTION_DESCRIPTION = 'board created from channel'
|
||||
const LINKED_CHANNEL = 'linked channel'
|
||||
const asTemplate = false
|
||||
|
||||
let boardsAndBlocks = undefined
|
||||
let boardsAndBlocks
|
||||
|
||||
if (templateIdRef.current === EMPTY_BOARD) {
|
||||
boardsAndBlocks = await mutator.addEmptyBoard(teamId, intl)
|
||||
@@ -102,7 +101,8 @@ const CreateBoardFromTemplate = (props: Props) => {
|
||||
}
|
||||
|
||||
const board = boardsAndBlocks.boards[0]
|
||||
await mutator.updateBoard({...board, channelId: channelId}, board, LINKED_CHANNEL)
|
||||
await mutator.updateBoard({...board, channelId}, board, LINKED_CHANNEL)
|
||||
|
||||
return board
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ const CreateBoardFromTemplate = (props: Props) => {
|
||||
if (wordBreakingIndex === -1) {
|
||||
return str
|
||||
}
|
||||
|
||||
return `${str.substring(0, (len + wordBreakingIndex))}…`
|
||||
}
|
||||
|
||||
@@ -143,6 +144,7 @@ const CreateBoardFromTemplate = (props: Props) => {
|
||||
|
||||
// do not show the description for the selected option so the input only show the icon and title of the template
|
||||
const selectedOption = id === optionLabel.selectValue[0]?.id
|
||||
|
||||
return (
|
||||
<div key={id}>
|
||||
<span className={`${cssPrefix}__icon`}>
|
||||
@@ -176,8 +178,9 @@ const CreateBoardFromTemplate = (props: Props) => {
|
||||
templates.push(emptyBoard)
|
||||
|
||||
if (value !== '') {
|
||||
templates = templates.filter(template => template.title.toLowerCase().includes(value.toLowerCase()))
|
||||
templates = templates.filter((template) => template.title.toLowerCase().includes(value.toLowerCase()))
|
||||
}
|
||||
|
||||
return templates
|
||||
}, [allTemplates])
|
||||
|
||||
@@ -202,10 +205,11 @@ const CreateBoardFromTemplate = (props: Props) => {
|
||||
}),
|
||||
valueContainer: (baseStyles: CSSObject): CSSObject => ({
|
||||
...baseStyles,
|
||||
overflow: 'visible'
|
||||
overflow: 'visible',
|
||||
}),
|
||||
placeholder: (baseStyles: CSSObject, state: PlaceholderProps<ReactSelectItem, false, GroupBase<ReactSelectItem>>): CSSObject => {
|
||||
const modifyPlaceholder = state.selectProps.menuIsOpen || (!state.selectProps.menuIsOpen && state.hasValue)
|
||||
|
||||
return {
|
||||
...baseStyles,
|
||||
position: 'absolute',
|
||||
@@ -257,12 +261,13 @@ const CreateBoardFromTemplate = (props: Props) => {
|
||||
|
||||
const IntlCreateBoardFromTemplate = (props: Props) => {
|
||||
const language = useAppSelector<string>(getLanguage)
|
||||
|
||||
return (
|
||||
<IntlProvider
|
||||
locale={language.split(/[_]/)[0]}
|
||||
messages={getMessages(language)}
|
||||
>
|
||||
<CreateBoardFromTemplate {...props}/>
|
||||
<CreateBoardFromTemplate {...props}/>
|
||||
</IntlProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useState, KeyboardEvent} from 'react'
|
||||
import React, {KeyboardEvent, useState} from 'react'
|
||||
|
||||
import {useIntl} from 'react-intl'
|
||||
|
||||
@@ -49,6 +49,7 @@ const CreateCategory = (props: Props): JSX.Element => {
|
||||
const onCreate = async (categoryName: string) => {
|
||||
if (!me) {
|
||||
Utils.logError('me not initialized')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,10 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
render(): React.ReactNode {
|
||||
if (this.state.hasError) {
|
||||
this.handleError()
|
||||
|
||||
return <span>{this.msg}</span>
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,12 @@
|
||||
import React from 'react'
|
||||
|
||||
import {
|
||||
render,
|
||||
act,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
fireEvent
|
||||
} from '@testing-library/react'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import {wrapIntl} from 'src/testUtils'
|
||||
|
||||
import FlashMessages, {sendFlashMessage} from './flashMessages'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useState, useEffect} from 'react'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {createNanoEvents} from 'nanoevents'
|
||||
|
||||
import './flashMessages.scss'
|
||||
@@ -37,6 +37,7 @@ const FlashMessages = (props: Props) => {
|
||||
setMessage(newMessage)
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
isSubscribed = false
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render, screen, fireEvent} from '@testing-library/react'
|
||||
import {fireEvent, render, screen} from '@testing-library/react'
|
||||
|
||||
import {Provider as ReduxProvider} from 'react-redux'
|
||||
|
||||
@@ -10,7 +10,7 @@ import userEvent from '@testing-library/user-event'
|
||||
|
||||
import {mocked} from 'jest-mock'
|
||||
|
||||
import {wrapDNDIntl, mockStateStore, blocksById} from 'src/testUtils'
|
||||
import {blocksById, mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||
|
||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useMemo, useCallback} from 'react'
|
||||
import React, {useCallback, useMemo} from 'react'
|
||||
import {FormattedMessage} from 'react-intl'
|
||||
|
||||
import {Constants, Permission} from 'src/constants'
|
||||
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user