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",
|
"formatjs",
|
||||||
"unused-imports",
|
"unused-imports",
|
||||||
"no-relative-import-paths",
|
"no-relative-import-paths",
|
||||||
"import-newlines"
|
"import-newlines",
|
||||||
|
"eslint-comments"
|
||||||
],
|
],
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"settings": {
|
"settings": {
|
||||||
@@ -27,21 +28,101 @@
|
|||||||
"line",
|
"line",
|
||||||
" Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n See LICENSE.txt for license information."
|
" Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n See LICENSE.txt for license information."
|
||||||
],
|
],
|
||||||
"max-lines": "off",
|
"array-bracket-spacing": [
|
||||||
"no-unused-expressions": 0,
|
|
||||||
"babel/no-unused-expressions": [
|
|
||||||
2,
|
2,
|
||||||
{
|
"never"
|
||||||
"allowShortCircuit": true
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"eol-last": [
|
"array-callback-return": 2,
|
||||||
|
"arrow-body-style": 0,
|
||||||
|
"arrow-parens": [
|
||||||
2,
|
2,
|
||||||
"always"
|
"always"
|
||||||
],
|
],
|
||||||
"import/no-unresolved": 2,
|
"arrow-spacing": [
|
||||||
"import/order": [
|
|
||||||
2,
|
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",
|
"newlines-between": "always-and-inside-groups",
|
||||||
"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,
|
"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": [
|
"react/prop-types": [
|
||||||
2,
|
2,
|
||||||
{
|
{
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"location",
|
"location",
|
||||||
"history",
|
"history",
|
||||||
"component"
|
"component",
|
||||||
|
"className"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"react/no-string-refs": 2,
|
"react/require-default-props": 0,
|
||||||
"no-only-tests/no-only-tests": [
|
"react/require-optimization": 1,
|
||||||
2,
|
"react/require-render-return": 2,
|
||||||
{
|
"react/self-closing-comp": 2,
|
||||||
"focus": [
|
"react/sort-comp": 0,
|
||||||
"only",
|
"react/style-prop-object": [
|
||||||
"skip"
|
2,
|
||||||
]
|
{
|
||||||
}
|
"allow": [
|
||||||
|
"FormattedNumber",
|
||||||
|
"FormattedDuration",
|
||||||
|
"FormattedRelativeTime",
|
||||||
|
"Timestamp"
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"max-nested-callbacks": [
|
"max-nested-callbacks": [
|
||||||
2,
|
2,
|
||||||
@@ -95,11 +562,84 @@
|
|||||||
2,
|
2,
|
||||||
3
|
3
|
||||||
],
|
],
|
||||||
"object-curly-spacing": [
|
"formatjs/no-multiple-whitespaces": 2,
|
||||||
|
"require-yield": 2,
|
||||||
|
"rest-spread-spacing": [
|
||||||
2,
|
2,
|
||||||
"never"
|
"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": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
@@ -206,7 +746,9 @@
|
|||||||
"global-require": 0,
|
"global-require": 0,
|
||||||
"new-cap": 0,
|
"new-cap": 0,
|
||||||
"prefer-arrow-callback": 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 = {
|
const config = {
|
||||||
transform: {
|
transform: {
|
||||||
"^.+\\.(t|j)sx?$": ["@swc/jest"]
|
'^.+\\.(t|j)sx?$': ['@swc/jest'],
|
||||||
},
|
},
|
||||||
moduleFileExtensions: [
|
moduleFileExtensions: [
|
||||||
"ts",
|
'ts',
|
||||||
"tsx",
|
'tsx',
|
||||||
"js",
|
'js',
|
||||||
"jsx",
|
'jsx',
|
||||||
"json",
|
'json',
|
||||||
"node"
|
'node',
|
||||||
],
|
],
|
||||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
"/nanoevents/",
|
'/nanoevents/',
|
||||||
"node_modules/(?!react-native|react-router|react-day-picker)"
|
'node_modules/(?!react-native|react-router|react-day-picker)',
|
||||||
],
|
],
|
||||||
testEnvironment: "jsdom",
|
testEnvironment: 'jsdom',
|
||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
"src/**/*.{ts,tsx,js,jsx}",
|
'src/**/*.{ts,tsx,js,jsx}',
|
||||||
"!src/test/**"
|
'!src/test/**',
|
||||||
],
|
],
|
||||||
testPathIgnorePatterns: [
|
testPathIgnorePatterns: [
|
||||||
"/node_modules/",
|
'/node_modules/',
|
||||||
],
|
],
|
||||||
clearMocks: true,
|
clearMocks: true,
|
||||||
coverageReporters: [
|
coverageReporters: [
|
||||||
"lcov",
|
'lcov',
|
||||||
"text-summary"
|
'text-summary',
|
||||||
],
|
],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
"^.+\\.(scss|css)$": "<rootDir>/src/test/style_mock.json",
|
'^.+\\.(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",
|
'\\.(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",
|
'\\.(scss|css)$': '<rootDir>/__mocks__/styleMock.js',
|
||||||
"^bundle-loader\\?lazy\\!(.*)$": "$1",
|
'^bundle-loader\\?lazy\\!(.*)$': '$1',
|
||||||
"^src(.*)$": "<rootDir>/src$1",
|
'^src(.*)$': '<rootDir>/src$1',
|
||||||
"^i18n(.*)$": "<rootDir>/i18n$1",
|
'^i18n(.*)$': '<rootDir>/i18n$1',
|
||||||
"^static(.*)$": "<rootDir>/static$1",
|
'^static(.*)$': '<rootDir>/static$1',
|
||||||
"^moment(.*)$": "<rootDir>/../node_modules/moment$1",
|
'^moment(.*)$': '<rootDir>/../node_modules/moment$1',
|
||||||
},
|
},
|
||||||
moduleDirectories: [
|
moduleDirectories: [
|
||||||
"src",
|
'src',
|
||||||
"node_modules",
|
'node_modules',
|
||||||
],
|
],
|
||||||
reporters: [
|
reporters: [
|
||||||
"default",
|
'default',
|
||||||
"jest-junit"
|
'jest-junit',
|
||||||
],
|
],
|
||||||
setupFiles: [
|
setupFiles: [
|
||||||
"jest-canvas-mock"
|
'jest-canvas-mock',
|
||||||
],
|
],
|
||||||
setupFilesAfterEnv: [
|
setupFilesAfterEnv: [
|
||||||
"<rootDir>/src/test/setup.tsx"
|
'<rootDir>/src/test/setup.tsx',
|
||||||
],
|
],
|
||||||
testTimeout: 60000,
|
testTimeout: 60000,
|
||||||
testEnvironmentOptions: {
|
testEnvironmentOptions: {
|
||||||
url: "http://localhost:8065"
|
url: 'http://localhost:8065',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
|||||||
@@ -19,5 +19,6 @@ module.exports = function loader(source) {
|
|||||||
newSource.push(line);
|
newSource.push(line);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return newSource.join('\n');
|
return newSource.join('\n');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -106,6 +106,8 @@
|
|||||||
"css-loader": "6.7.1",
|
"css-loader": "6.7.1",
|
||||||
"eslint-import-resolver-webpack": "0.13.2",
|
"eslint-import-resolver-webpack": "0.13.2",
|
||||||
"eslint-plugin-babel": "^5.3.1",
|
"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-header": "3.1.1",
|
||||||
"eslint-plugin-import": "2.25.4",
|
"eslint-plugin-import": "2.25.4",
|
||||||
"eslint-plugin-import-newlines": "1.3.1",
|
"eslint-plugin-import-newlines": "1.3.1",
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import FlashMessages from './components/flashMessages'
|
|||||||
import NewVersionBanner from './components/newVersionBanner'
|
import NewVersionBanner from './components/newVersionBanner'
|
||||||
import {Utils} from './utils'
|
import {Utils} from './utils'
|
||||||
import {fetchMe, getMe} from './store/users'
|
import {fetchMe, getMe} from './store/users'
|
||||||
import {getLanguage, fetchLanguage} from './store/language'
|
import {fetchLanguage, getLanguage} from './store/language'
|
||||||
import {useAppSelector, useAppDispatch} from './store/hooks'
|
import {useAppDispatch, useAppSelector} from './store/hooks'
|
||||||
import {fetchClientConfig} from './store/clientConfig'
|
import {fetchClientConfig} from './store/clientConfig'
|
||||||
import FocalboardRouter from './router'
|
import FocalboardRouter from './router'
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ class Archiver {
|
|||||||
private static exportArchive(prom: Promise<Response>): void {
|
private static exportArchive(prom: Promise<Response>): void {
|
||||||
// TODO: don't download whole archive before presenting SaveAs dialog.
|
// TODO: don't download whole archive before presenting SaveAs dialog.
|
||||||
prom.then((response) => {
|
prom.then((response) => {
|
||||||
response.blob().
|
response.blob()
|
||||||
then((blob) => {
|
.then((blob) => {
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.style.display = 'none'
|
link.style.display = 'none'
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class BlockIcons {
|
|||||||
randomIcon(): string {
|
randomIcon(): string {
|
||||||
const index = Math.floor(Math.random() * randomEmojiList.length)
|
const index = Math.floor(Math.random() * randomEmojiList.length)
|
||||||
const icon = randomEmojiList[index]
|
const icon = randomEmojiList[index]
|
||||||
|
|
||||||
return icon
|
return icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||||
|
|
||||||
import {createPatchesFromBlocks, createBlock} from './block'
|
import {createBlock, createPatchesFromBlocks} from './block'
|
||||||
|
|
||||||
describe('block tests', () => {
|
describe('block tests', () => {
|
||||||
const board = TestBlockFactory.createBoard()
|
const board = TestBlockFactory.createBoard()
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ interface FileInfo {
|
|||||||
|
|
||||||
function createBlock(block?: Block): Block {
|
function createBlock(block?: Block): Block {
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: block?.id || Utils.createGuid(Utils.blockTypeToIDType(block?.type)),
|
id: block?.id || Utils.createGuid(Utils.blockTypeToIDType(block?.type)),
|
||||||
schema: 1,
|
schema: 1,
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createPatchesFromBoards,
|
|
||||||
createBoard,
|
|
||||||
IPropertyTemplate,
|
IPropertyTemplate,
|
||||||
createPatchesFromBoardsAndBlocks
|
createBoard,
|
||||||
|
createPatchesFromBoards,
|
||||||
|
createPatchesFromBoardsAndBlocks,
|
||||||
} from './board'
|
} from './board'
|
||||||
import {createBlock} from './block'
|
import {createBlock} from './block'
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import difference from 'lodash/difference'
|
import difference from 'lodash/difference'
|
||||||
|
|
||||||
import {Utils, IDType} from 'src/utils'
|
import {IDType, Utils} from 'src/utils'
|
||||||
|
|
||||||
import {Block, BlockPatch, createPatchesFromBlocks} from './block'
|
import {Block, BlockPatch, createPatchesFromBlocks} from './block'
|
||||||
import {Card} from './card'
|
import {Card} from './card'
|
||||||
@@ -54,7 +54,7 @@ type BoardPatch = {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
updatedProperties?: Record<string, any>
|
updatedProperties?: Record<string, any>
|
||||||
deletedProperties?: string[]
|
deletedProperties?: string[]
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
updatedCardProperties?: IPropertyTemplate[]
|
updatedCardProperties?: IPropertyTemplate[]
|
||||||
deletedCardProperties?: string[]
|
deletedCardProperties?: string[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ function createCard(block?: Block): Card {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...createBlock(block),
|
...createBlock(block),
|
||||||
type: 'card',
|
type: 'card',
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ function createFilterGroup(o?: FilterGroup): FilterGroup {
|
|||||||
if (isAFilterGroupInstance(p)) {
|
if (isAFilterGroupInstance(p)) {
|
||||||
return createFilterGroup(p)
|
return createFilterGroup(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
return createFilterClause(p)
|
return createFilterClause(p)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
operation: o?.operation || 'and',
|
operation: o?.operation || 'and',
|
||||||
filters,
|
filters,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import {Card} from './blocks/card'
|
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[] {
|
function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty?: IPropertyTemplate): BoardGroup[] {
|
||||||
const groups = []
|
const groups = []
|
||||||
@@ -22,6 +22,7 @@ function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty
|
|||||||
// Empty group
|
// Empty group
|
||||||
const emptyGroupCards = cards.filter((card) => {
|
const emptyGroupCards = cards.filter((card) => {
|
||||||
const groupByOptionId = card.fields.properties[groupByProperty?.id || '']
|
const groupByOptionId = card.fields.properties[groupByProperty?.id || '']
|
||||||
|
|
||||||
return !groupByOptionId || !groupByProperty?.options.find((option) => option.id === groupByOptionId)
|
return !groupByOptionId || !groupByProperty?.options.find((option) => option.id === groupByOptionId)
|
||||||
})
|
})
|
||||||
const group: BoardGroup = {
|
const group: BoardGroup = {
|
||||||
@@ -31,15 +32,16 @@ function groupCardsByOptions(cards: Card[], optionIds: string[], groupByProperty
|
|||||||
groups.push(group)
|
groups.push(group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOptionGroups(cards: Card[], visibleOptionIds: string[], hiddenOptionIds: string[], groupByProperty?: IPropertyTemplate): {visible: BoardGroup[], hidden: BoardGroup[]} {
|
function getOptionGroups(cards: Card[], visibleOptionIds: string[], hiddenOptionIds: string[], groupByProperty?: IPropertyTemplate): {visible: BoardGroup[], hidden: BoardGroup[]} {
|
||||||
let unassignedOptionIds: string[] = []
|
let unassignedOptionIds: string[] = []
|
||||||
if (groupByProperty) {
|
if (groupByProperty) {
|
||||||
unassignedOptionIds = groupByProperty.options.
|
unassignedOptionIds = groupByProperty.options
|
||||||
filter((o: IPropertyOption) => !visibleOptionIds.includes(o.id) && !hiddenOptionIds.includes(o.id)).
|
.filter((o: IPropertyOption) => !visibleOptionIds.includes(o.id) && !hiddenOptionIds.includes(o.id))
|
||||||
map((o: IPropertyOption) => o.id)
|
.map((o: IPropertyOption) => o.id)
|
||||||
}
|
}
|
||||||
const allVisibleOptionIds = [...visibleOptionIds, ...unassignedOptionIds]
|
const allVisibleOptionIds = [...visibleOptionIds, ...unassignedOptionIds]
|
||||||
|
|
||||||
@@ -50,6 +52,7 @@ function getOptionGroups(cards: Card[], visibleOptionIds: string[], hiddenOption
|
|||||||
|
|
||||||
const visibleGroups = groupCardsByOptions(cards, allVisibleOptionIds, groupByProperty)
|
const visibleGroups = groupCardsByOptions(cards, allVisibleOptionIds, groupByProperty)
|
||||||
const hiddenGroups = groupCardsByOptions(cards, hiddenOptionIds, groupByProperty)
|
const hiddenGroups = groupCardsByOptions(cards, hiddenOptionIds, groupByProperty)
|
||||||
|
|
||||||
return {visible: visibleGroups, hidden: hiddenGroups}
|
return {visible: visibleGroups, hidden: hiddenGroups}
|
||||||
}
|
}
|
||||||
export function getVisibleAndHiddenGroups(cards: Card[], visibleOptionIds: string[], hiddenOptionIds: string[], groupByProperty?: IPropertyTemplate): {visible: BoardGroup[], hidden: BoardGroup[]} {
|
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] ?? []
|
const curGroup = unique[key] ?? []
|
||||||
|
|
||||||
return {...unique, [key]: [...curGroup, item]}
|
return {...unique, [key]: [...curGroup, item]}
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class CardFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateProperty
|
return dateProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ class CardFilter {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
Utils.assert(filterGroup.operation === 'and')
|
Utils.assert(filterGroup.operation === 'and')
|
||||||
@@ -63,6 +65,7 @@ class CardFilter {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,12 +98,14 @@ class CardFilter {
|
|||||||
if (filter.values?.length < 1) {
|
if (filter.values?.length < 1) {
|
||||||
break
|
break
|
||||||
} // No values = ignore clause (always met)
|
} // No values = ignore clause (always met)
|
||||||
|
|
||||||
return (filter.values.find((cValue) => (Array.isArray(value) ? value.includes(cValue) : cValue === value)) !== undefined)
|
return (filter.values.find((cValue) => (Array.isArray(value) ? value.includes(cValue) : cValue === value)) !== undefined)
|
||||||
}
|
}
|
||||||
case 'notIncludes': {
|
case 'notIncludes': {
|
||||||
if (filter.values?.length < 1) {
|
if (filter.values?.length < 1) {
|
||||||
break
|
break
|
||||||
} // No values = ignore clause (always met)
|
} // No values = ignore clause (always met)
|
||||||
|
|
||||||
return (filter.values.find((cValue) => (Array.isArray(value) ? value.includes(cValue) : cValue === value)) === undefined)
|
return (filter.values.find((cValue) => (Array.isArray(value) ? value.includes(cValue) : cValue === value)) === undefined)
|
||||||
}
|
}
|
||||||
case 'isEmpty': {
|
case 'isEmpty': {
|
||||||
@@ -128,50 +133,59 @@ class CardFilter {
|
|||||||
if (dateValue.from) {
|
if (dateValue.from) {
|
||||||
return dateValue.from > (numericFilter - halfDay) && dateValue.from < (numericFilter + halfDay)
|
return dateValue.from > (numericFilter - halfDay) && dateValue.from < (numericFilter + halfDay)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dateValue.from && dateValue.to) {
|
if (dateValue.from && dateValue.to) {
|
||||||
return dateValue.from <= numericFilter && dateValue.to >= numericFilter
|
return dateValue.from <= numericFilter && dateValue.to >= numericFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateValue.from === numericFilter
|
return dateValue.from === numericFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
return filter.values[0]?.toLowerCase() === value
|
return filter.values[0]?.toLowerCase() === value
|
||||||
}
|
}
|
||||||
case 'contains': {
|
case 'contains': {
|
||||||
if (filter.values.length === 0) {
|
if (filter.values.length === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return (value as string || '').includes(filter.values[0]?.toLowerCase())
|
return (value as string || '').includes(filter.values[0]?.toLowerCase())
|
||||||
}
|
}
|
||||||
case 'notContains': {
|
case 'notContains': {
|
||||||
if (filter.values.length === 0) {
|
if (filter.values.length === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return !(value as string || '').includes(filter.values[0]?.toLowerCase())
|
return !(value as string || '').includes(filter.values[0]?.toLowerCase())
|
||||||
}
|
}
|
||||||
case 'startsWith': {
|
case 'startsWith': {
|
||||||
if (filter.values.length === 0) {
|
if (filter.values.length === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return (value as string || '').startsWith(filter.values[0]?.toLowerCase())
|
return (value as string || '').startsWith(filter.values[0]?.toLowerCase())
|
||||||
}
|
}
|
||||||
case 'notStartsWith': {
|
case 'notStartsWith': {
|
||||||
if (filter.values.length === 0) {
|
if (filter.values.length === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return !(value as string || '').startsWith(filter.values[0]?.toLowerCase())
|
return !(value as string || '').startsWith(filter.values[0]?.toLowerCase())
|
||||||
}
|
}
|
||||||
case 'endsWith': {
|
case 'endsWith': {
|
||||||
if (filter.values.length === 0) {
|
if (filter.values.length === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return (value as string || '').endsWith(filter.values[0]?.toLowerCase())
|
return (value as string || '').endsWith(filter.values[0]?.toLowerCase())
|
||||||
}
|
}
|
||||||
case 'notEndsWith': {
|
case 'notEndsWith': {
|
||||||
if (filter.values.length === 0) {
|
if (filter.values.length === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return !(value as string || '').endsWith(filter.values[0]?.toLowerCase())
|
return !(value as string || '').endsWith(filter.values[0]?.toLowerCase())
|
||||||
}
|
}
|
||||||
case 'isBefore': {
|
case 'isBefore': {
|
||||||
@@ -187,11 +201,13 @@ class CardFilter {
|
|||||||
if (dateValue.from) {
|
if (dateValue.from) {
|
||||||
return dateValue.from < (numericFilter - halfDay)
|
return dateValue.from < (numericFilter - halfDay)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateValue.from ? dateValue.from < numericFilter : false
|
return dateValue.from ? dateValue.from < numericFilter : false
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case 'isAfter': {
|
case 'isAfter': {
|
||||||
@@ -207,14 +223,17 @@ class CardFilter {
|
|||||||
if (dateValue.from) {
|
if (dateValue.from) {
|
||||||
return dateValue.from > (numericFilter + halfDay)
|
return dateValue.from > (numericFilter + halfDay)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dateValue.to) {
|
if (dateValue.to) {
|
||||||
return dateValue.to > numericFilter
|
return dateValue.to > numericFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateValue.from ? dateValue.from > numericFilter : false
|
return dateValue.from ? dateValue.from > numericFilter : false
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,6 +241,7 @@ class CardFilter {
|
|||||||
Utils.assertFailure(`Invalid filter condition ${filter.condition}`)
|
Utils.assertFailure(`Invalid filter condition ${filter.condition}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +263,7 @@ class CardFilter {
|
|||||||
if (property.value) {
|
if (property.value) {
|
||||||
result[property.id] = property.value
|
result[property.id] = property.value
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +275,7 @@ class CardFilter {
|
|||||||
result[property.id] = property.value
|
result[property.id] = property.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +283,7 @@ class CardFilter {
|
|||||||
const template = templates.find((o) => o.id === filterClause.propertyId)
|
const template = templates.find((o) => o.id === filterClause.propertyId)
|
||||||
if (!template) {
|
if (!template) {
|
||||||
Utils.assertFailure(`propertyThatMeetsFilterClause. Cannot find template: ${filterClause.propertyId}`)
|
Utils.assertFailure(`propertyThatMeetsFilterClause. Cannot find template: ${filterClause.propertyId}`)
|
||||||
|
|
||||||
return {id: filterClause.propertyId}
|
return {id: filterClause.propertyId}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +296,7 @@ class CardFilter {
|
|||||||
if (filterClause.values.length < 1) {
|
if (filterClause.values.length < 1) {
|
||||||
return {id: filterClause.propertyId}
|
return {id: filterClause.propertyId}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {id: filterClause.propertyId, value: filterClause.values[0]}
|
return {id: filterClause.propertyId, value: filterClause.values[0]}
|
||||||
}
|
}
|
||||||
case 'notIncludes': {
|
case 'notIncludes': {
|
||||||
@@ -285,8 +309,10 @@ class CardFilter {
|
|||||||
if (template.type === 'select') {
|
if (template.type === 'select') {
|
||||||
if (template.options.length > 0) {
|
if (template.options.length > 0) {
|
||||||
const option = template.options[0]
|
const option = template.options[0]
|
||||||
|
|
||||||
return {id: filterClause.propertyId, value: option.id}
|
return {id: filterClause.propertyId, value: option.id}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {id: filterClause.propertyId}
|
return {id: filterClause.propertyId}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import React, {ReactElement, ReactNode} from 'react'
|
import React, {ReactElement, ReactNode} from 'react'
|
||||||
import {render, screen, waitFor} from '@testing-library/react'
|
import {render, screen, waitFor} from '@testing-library/react'
|
||||||
|
|
||||||
|
|
||||||
import {mocked} from 'jest-mock'
|
import {mocked} from 'jest-mock'
|
||||||
|
|
||||||
import userEvent from '@testing-library/user-event'
|
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(console.error).toBeCalledWith(expect.stringContaining('addContentMenu, unknown content type: unknown'))
|
||||||
expect(container).toMatchSnapshot()
|
expect(container).toMatchSnapshot()
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {useIntl} from 'react-intl'
|
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 {Card} from 'src/blocks/card'
|
||||||
import mutator from 'src/mutator'
|
import mutator from 'src/mutator'
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
@@ -27,6 +27,7 @@ const AddContentMenuItem = (props: Props): JSX.Element => {
|
|||||||
const handler = contentRegistry.getHandler(type)
|
const handler = contentRegistry.getHandler(type)
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
Utils.logError(`addContentMenu, unknown content type: ${type}`)
|
Utils.logError(`addContentMenu, unknown content type: ${type}`)
|
||||||
|
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {fireEvent, render, screen} from '@testing-library/react'
|
|||||||
|
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
|
|
||||||
|
|
||||||
import {mocked} from 'jest-mock'
|
import {mocked} from 'jest-mock'
|
||||||
|
|
||||||
import mutator from 'src/mutator'
|
import mutator from 'src/mutator'
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Provider as ReduxProvider} from 'react-redux'
|
import {Provider as ReduxProvider} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
|
act,
|
||||||
|
fireEvent,
|
||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
fireEvent,
|
|
||||||
act
|
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mockDOM,
|
mockDOM,
|
||||||
wrapDNDIntl,
|
|
||||||
mockStateStore,
|
mockStateStore,
|
||||||
setup
|
setup,
|
||||||
|
wrapDNDIntl,
|
||||||
} from 'src/testUtils'
|
} from 'src/testUtils'
|
||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
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 () => {
|
test('should call onSave on hit enter in the input', async () => {
|
||||||
const onSave = jest.fn()
|
const onSave = jest.fn()
|
||||||
|
|
||||||
const {user} = setup(wrapDNDIntl(
|
const {user} = setup(wrapDNDIntl(
|
||||||
<ReduxProvider store={store}>
|
<ReduxProvider store={store}>
|
||||||
<BlockContent
|
<BlockContent
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ function BlockContent(props: Props) {
|
|||||||
const updatedBlock = await onSave(b)
|
const updatedBlock = await onSave(b)
|
||||||
props.setEditing(null)
|
props.setEditing(null)
|
||||||
props.setAfterBlock(updatedBlock)
|
props.setAfterBlock(updatedBlock)
|
||||||
|
|
||||||
return updatedBlock
|
return updatedBlock
|
||||||
}}
|
}}
|
||||||
id={block.id}
|
id={block.id}
|
||||||
@@ -73,6 +74,7 @@ function BlockContent(props: Props) {
|
|||||||
const contentType = registry.get(block.contentType)
|
const contentType = registry.get(block.contentType)
|
||||||
if (contentType && contentType.Display) {
|
if (contentType && contentType.Display) {
|
||||||
const DisplayContent = contentType.Display
|
const DisplayContent = contentType.Display
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={drop}
|
ref={drop}
|
||||||
@@ -118,6 +120,7 @@ function BlockContent(props: Props) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
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 {mocked} from 'jest-mock'
|
||||||
|
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {render, screen, fireEvent} from '@testing-library/react'
|
import {fireEvent, render, screen} from '@testing-library/react'
|
||||||
|
|
||||||
import CheckboxBlock from '.'
|
import CheckboxBlock from '.'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useRef, useEffect} from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import {marked} from 'marked'
|
import {marked} from 'marked'
|
||||||
|
|
||||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
@@ -23,6 +23,7 @@ const Checkbox: ContentType<ValueType> = {
|
|||||||
Display: (props: BlockInputProps<ValueType>) => {
|
Display: (props: BlockInputProps<ValueType>) => {
|
||||||
const renderer = new marked.Renderer()
|
const renderer = new marked.Renderer()
|
||||||
const html = marked(props.value.value || '', {renderer, breaks: true})
|
const html = marked(props.value.value || '', {renderer, breaks: true})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='CheckboxView'>
|
<div className='CheckboxView'>
|
||||||
<input
|
<input
|
||||||
@@ -46,6 +47,7 @@ const Checkbox: ContentType<ValueType> = {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.focus()
|
ref.current?.focus()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='Checkbox'>
|
<div className='Checkbox'>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const Divider: ContentType = {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
props.onSave(props.value)
|
props.onSave(props.value)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {render, screen, fireEvent} from '@testing-library/react'
|
import {fireEvent, render, screen} from '@testing-library/react'
|
||||||
|
|
||||||
import H1Block from '.'
|
import H1Block from '.'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useRef, useEffect} from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import {marked} from 'marked'
|
import {marked} from 'marked'
|
||||||
|
|
||||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
@@ -17,6 +17,7 @@ const H1: ContentType = {
|
|||||||
Display: (props: BlockInputProps) => {
|
Display: (props: BlockInputProps) => {
|
||||||
const renderer = new marked.Renderer()
|
const renderer = new marked.Renderer()
|
||||||
const html = marked('# ' + props.value, {renderer, breaks: true})
|
const html = marked('# ' + props.value, {renderer, breaks: true})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{__html: html.trim()}}
|
dangerouslySetInnerHTML={{__html: html.trim()}}
|
||||||
@@ -28,6 +29,7 @@ const H1: ContentType = {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.focus()
|
ref.current?.focus()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {render, screen, fireEvent} from '@testing-library/react'
|
import {fireEvent, render, screen} from '@testing-library/react'
|
||||||
|
|
||||||
import H2Block from '.'
|
import H2Block from '.'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useRef, useEffect} from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import {marked} from 'marked'
|
import {marked} from 'marked'
|
||||||
|
|
||||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
@@ -17,6 +17,7 @@ const H2: ContentType = {
|
|||||||
Display: (props: BlockInputProps) => {
|
Display: (props: BlockInputProps) => {
|
||||||
const renderer = new marked.Renderer()
|
const renderer = new marked.Renderer()
|
||||||
const html = marked('## ' + props.value, {renderer, breaks: true})
|
const html = marked('## ' + props.value, {renderer, breaks: true})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{__html: html.trim()}}
|
dangerouslySetInnerHTML={{__html: html.trim()}}
|
||||||
@@ -28,6 +29,7 @@ const H2: ContentType = {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.focus()
|
ref.current?.focus()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {render, screen, fireEvent} from '@testing-library/react'
|
import {fireEvent, render, screen} from '@testing-library/react'
|
||||||
|
|
||||||
import H3Block from '.'
|
import H3Block from '.'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useRef, useEffect} from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import {marked} from 'marked'
|
import {marked} from 'marked'
|
||||||
|
|
||||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
@@ -17,6 +17,7 @@ const H3: ContentType = {
|
|||||||
Display: (props: BlockInputProps) => {
|
Display: (props: BlockInputProps) => {
|
||||||
const renderer = new marked.Renderer()
|
const renderer = new marked.Renderer()
|
||||||
const html = marked('### ' + props.value, {renderer, breaks: true})
|
const html = marked('### ' + props.value, {renderer, breaks: true})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{__html: html.trim()}}
|
dangerouslySetInnerHTML={{__html: html.trim()}}
|
||||||
@@ -28,6 +29,7 @@ const H3: ContentType = {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.focus()
|
ref.current?.focus()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
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 {mocked} from 'jest-mock'
|
||||||
|
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
@@ -44,6 +44,7 @@ const Image: ContentType<FileInfo> = {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
Input: (props: BlockInputProps<FileInfo>) => {
|
Input: (props: BlockInputProps<FileInfo>) => {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export function isSubPrefix(text: string): boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ export function getBySlashCommandPrefix(slashCommandPrefix: string): ContentType
|
|||||||
return ct
|
return ct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ const ListItem: ContentType = {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.focus()
|
ref.current?.focus()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {render, screen, fireEvent} from '@testing-library/react'
|
import {fireEvent, render, screen} from '@testing-library/react'
|
||||||
|
|
||||||
import ListItemBlock from '.'
|
import ListItemBlock from '.'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useRef, useEffect} from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import {marked} from 'marked'
|
import {marked} from 'marked'
|
||||||
|
|
||||||
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
import {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
@@ -15,6 +15,7 @@ const Quote: ContentType = {
|
|||||||
Display: (props: BlockInputProps) => {
|
Display: (props: BlockInputProps) => {
|
||||||
const renderer = new marked.Renderer()
|
const renderer = new marked.Renderer()
|
||||||
const html = marked('> ' + props.value, {renderer, breaks: true})
|
const html = marked('> ' + props.value, {renderer, breaks: true})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='Quote'
|
className='Quote'
|
||||||
@@ -30,6 +31,7 @@ const Quote: ContentType = {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.focus()
|
ref.current?.focus()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<blockquote
|
<blockquote
|
||||||
className='Quote'
|
className='Quote'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {render, screen, fireEvent} from '@testing-library/react'
|
import {fireEvent, render, screen} from '@testing-library/react'
|
||||||
|
|
||||||
import QuoteBlock from '.'
|
import QuoteBlock from '.'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
import {Utils} from 'src/utils'
|
import {Utils} from 'src/utils'
|
||||||
@@ -16,6 +16,7 @@ const Text: ContentType = {
|
|||||||
editable: true,
|
editable: true,
|
||||||
Display: (props: BlockInputProps) => {
|
Display: (props: BlockInputProps) => {
|
||||||
const html: string = Utils.htmlFromMarkdown(props.value || '')
|
const html: string = Utils.htmlFromMarkdown(props.value || '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{__html: html}}
|
dangerouslySetInnerHTML={{__html: html}}
|
||||||
@@ -28,6 +29,7 @@ const Text: ContentType = {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.focus()
|
ref.current?.focus()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const TextContent: ContentType = {
|
|||||||
editable: true,
|
editable: true,
|
||||||
Display: (props: BlockInputProps) => {
|
Display: (props: BlockInputProps) => {
|
||||||
const html: string = Utils.htmlFromMarkdown(props.value || '')
|
const html: string = Utils.htmlFromMarkdown(props.value || '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{__html: html}}
|
dangerouslySetInnerHTML={{__html: html}}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Provider as ReduxProvider} from 'react-redux'
|
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 {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||||
|
|
||||||
import TextBlock from '.'
|
import TextBlock from '.'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 {BlockInputProps, ContentType} from 'src/components/blocksEditor/blocks/types'
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
@@ -49,6 +49,7 @@ const Video: ContentType<FileInfo> = {
|
|||||||
</video>
|
</video>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
Input: (props: BlockInputProps<FileInfo>) => {
|
Input: (props: BlockInputProps<FileInfo>) => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
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 {mocked} from 'jest-mock'
|
||||||
|
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
|
|||||||
@@ -4,21 +4,20 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Provider as ReduxProvider} from 'react-redux'
|
import {Provider as ReduxProvider} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
|
act,
|
||||||
|
fireEvent,
|
||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
fireEvent,
|
|
||||||
act
|
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mockDOM,
|
mockDOM,
|
||||||
wrapDNDIntl,
|
|
||||||
mockStateStore,
|
mockStateStore,
|
||||||
setup
|
setup,
|
||||||
|
wrapDNDIntl,
|
||||||
} from 'src/testUtils'
|
} from 'src/testUtils'
|
||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||||
|
|
||||||
|
|
||||||
import {BlockData} from './blocks/types'
|
import {BlockData} from './blocks/types'
|
||||||
import BlocksEditor from './blocksEditor'
|
import BlocksEditor from './blocksEditor'
|
||||||
|
|
||||||
@@ -120,7 +119,6 @@ describe('components/blocksEditor/blocksEditor', () => {
|
|||||||
await user.keyboard('{Enter}')
|
await user.keyboard('{Enter}')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
expect(onBlockCreated).toBeCalledWith(expect.objectContaining({value: 'test'}))
|
expect(onBlockCreated).toBeCalledWith(expect.objectContaining({value: 'test'}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useState, useMemo} from 'react'
|
import React, {useMemo, useState} from 'react'
|
||||||
import {DndProvider} from 'react-dnd'
|
import {DndProvider} from 'react-dnd'
|
||||||
import {HTML5Backend} from 'react-dnd-html5-backend'
|
import {HTML5Backend} from 'react-dnd-html5-backend'
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ function BlocksEditor(props: Props) {
|
|||||||
const [editing, setEditing] = useState<BlockData|null>(null)
|
const [editing, setEditing] = useState<BlockData|null>(null)
|
||||||
const [afterBlock, setAfterBlock] = 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])
|
const contentOrder = useMemo(() => props.blocks.filter((b) => b.id).map((b) => b.id!), [props.blocks])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='BlocksEditor'
|
className='BlocksEditor'
|
||||||
@@ -34,6 +35,7 @@ function BlocksEditor(props: Props) {
|
|||||||
setEditing(afterBlock)
|
setEditing(afterBlock)
|
||||||
}
|
}
|
||||||
setAfterBlock(null)
|
setAfterBlock(null)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let prevBlock = null
|
let prevBlock = null
|
||||||
@@ -97,6 +99,7 @@ function BlocksEditor(props: Props) {
|
|||||||
const newBlock = await props.onBlockModified(b)
|
const newBlock = await props.onBlockModified(b)
|
||||||
setNextType(registry.get(b.contentType).nextType || '')
|
setNextType(registry.get(b.contentType).nextType || '')
|
||||||
setAfterBlock(newBlock)
|
setAfterBlock(newBlock)
|
||||||
|
|
||||||
return newBlock
|
return newBlock
|
||||||
}}
|
}}
|
||||||
onMove={props.onBlockMoved}
|
onMove={props.onBlockMoved}
|
||||||
@@ -109,6 +112,7 @@ function BlocksEditor(props: Props) {
|
|||||||
const newBlock = await props.onBlockCreated(b, afterBlock)
|
const newBlock = await props.onBlockCreated(b, afterBlock)
|
||||||
setNextType(registry.get(b.contentType).nextType || '')
|
setNextType(registry.get(b.contentType).nextType || '')
|
||||||
setAfterBlock(newBlock)
|
setAfterBlock(newBlock)
|
||||||
|
|
||||||
return newBlock
|
return newBlock
|
||||||
}}
|
}}
|
||||||
/>)}
|
/>)}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Provider as ReduxProvider} from 'react-redux'
|
import {Provider as ReduxProvider} from 'react-redux'
|
||||||
import {render, screen, act} from '@testing-library/react'
|
import {act, render, screen} from '@testing-library/react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mockDOM,
|
mockDOM,
|
||||||
wrapDNDIntl,
|
|
||||||
mockStateStore,
|
mockStateStore,
|
||||||
setup
|
setup,
|
||||||
|
wrapDNDIntl,
|
||||||
} from 'src/testUtils'
|
} from 'src/testUtils'
|
||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useState, useEffect} from 'react'
|
import React, {useEffect, useState} from 'react'
|
||||||
|
|
||||||
import * as contentBlocks from './blocks/'
|
import * as contentBlocks from './blocks/'
|
||||||
import {ContentType, BlockData} from './blocks/types'
|
import {BlockData, ContentType} from './blocks/types'
|
||||||
import RootInput from './rootInput'
|
import RootInput from './rootInput'
|
||||||
|
|
||||||
import './editor.scss'
|
import './editor.scss'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {render, screen, fireEvent} from '@testing-library/react'
|
import {fireEvent, render, screen} from '@testing-library/react'
|
||||||
|
|
||||||
import RootInput from './rootInput'
|
import RootInput from './rootInput'
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Provider as ReduxProvider} from 'react-redux'
|
import {Provider as ReduxProvider} from 'react-redux'
|
||||||
import {
|
import {
|
||||||
|
act,
|
||||||
|
fireEvent,
|
||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
act,
|
|
||||||
fireEvent
|
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
import {mocked} from 'jest-mock'
|
import {mocked} from 'jest-mock'
|
||||||
|
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
|
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
import {mockStateStore} from 'src/testUtils'
|
import {mockStateStore, wrapIntl} from 'src/testUtils'
|
||||||
import {createBoard} from 'src/blocks/board'
|
import {createBoard} from 'src/blocks/board'
|
||||||
import {wrapIntl} from 'src/testUtils'
|
|
||||||
|
|
||||||
import BoardSelector from './boardSelector'
|
import BoardSelector from './boardSelector'
|
||||||
|
|
||||||
@@ -97,7 +96,7 @@ describe('components/boardSelector', () => {
|
|||||||
expect(container).toMatchSnapshot()
|
expect(container).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("escape button should unmount the component", () => {
|
it('escape button should unmount the component', () => {
|
||||||
mockedOctoClient.searchLinkableBoards.mockResolvedValueOnce([])
|
mockedOctoClient.searchLinkableBoards.mockResolvedValueOnce([])
|
||||||
|
|
||||||
const store = mockStateStore([], state)
|
const store = mockStateStore([], state)
|
||||||
@@ -114,10 +113,10 @@ describe('components/boardSelector', () => {
|
|||||||
expect(store.dispatch).toHaveBeenCalledTimes(0)
|
expect(store.dispatch).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
fireEvent.keyDown(getByText(/Link boards/i), {
|
fireEvent.keyDown(getByText(/Link boards/i), {
|
||||||
key: "Escape",
|
key: 'Escape',
|
||||||
code: "Escape",
|
code: 'Escape',
|
||||||
keyCode: 27,
|
keyCode: 27,
|
||||||
charCode: 27
|
charCode: 27,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(store.dispatch).toHaveBeenCalledTimes(2)
|
expect(store.dispatch).toHaveBeenCalledTimes(2)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useState, useMemo, useCallback} from 'react'
|
import React, {useCallback, useMemo, useState} from 'react'
|
||||||
import {IntlProvider, useIntl, FormattedMessage} from 'react-intl'
|
import {FormattedMessage, IntlProvider, useIntl} from 'react-intl'
|
||||||
import debounce from 'lodash/debounce'
|
import debounce from 'lodash/debounce'
|
||||||
|
|
||||||
import {SuiteWindow} from 'src/types/index'
|
import {SuiteWindow} from 'src/types/index'
|
||||||
@@ -13,10 +13,10 @@ import {useWebsockets} from 'src/hooks/websockets'
|
|||||||
|
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
import mutator from 'src/mutator'
|
import mutator from 'src/mutator'
|
||||||
import {getCurrentTeamId, getAllTeams, Team} from 'src/store/teams'
|
import {Team, getAllTeams, getCurrentTeamId} from 'src/store/teams'
|
||||||
import {createBoard, Board} from 'src/blocks/board'
|
import {Board, createBoard} from 'src/blocks/board'
|
||||||
import {useAppSelector, useAppDispatch} from 'src/store/hooks'
|
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
||||||
import {EmptySearch, EmptyResults} from 'src/components/searchDialog/searchDialog'
|
import {EmptyResults, EmptySearch} from 'src/components/searchDialog/searchDialog'
|
||||||
import ConfirmationDialog from 'src/components/confirmationDialogBox'
|
import ConfirmationDialog from 'src/components/confirmationDialogBox'
|
||||||
import Dialog from 'src/components/dialog'
|
import Dialog from 'src/components/dialog'
|
||||||
import SearchIcon from 'src/widgets/icons/search'
|
import SearchIcon from 'src/widgets/icons/search'
|
||||||
@@ -67,7 +67,7 @@ const BoardSelector = () => {
|
|||||||
let updated = false
|
let updated = false
|
||||||
results.forEach((board, idx) => {
|
results.forEach((board, idx) => {
|
||||||
for (const newBoard of boards) {
|
for (const newBoard of boards) {
|
||||||
if (newBoard.id == board.id) {
|
if (newBoard.id === board.id) {
|
||||||
newResults[idx] = newBoard
|
newResults[idx] = newBoard
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
@@ -95,6 +95,7 @@ const BoardSelector = () => {
|
|||||||
const linkBoard = async (board: Board, confirmed?: boolean): Promise<void> => {
|
const linkBoard = async (board: Board, confirmed?: boolean): Promise<void> => {
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
setShowLinkBoardConfirmation(board)
|
setShowLinkBoardConfirmation(board)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newBoard = createBoard({...board, channelId: currentChannel})
|
const newBoard = createBoard({...board, channelId: currentChannel})
|
||||||
@@ -117,7 +118,7 @@ const BoardSelector = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let confirmationSubText
|
let confirmationSubText
|
||||||
if (showLinkBoardConfirmation?.channelId !== '') {
|
if (showLinkBoardConfirmation?.channelId) {
|
||||||
confirmationSubText = intl.formatMessage({
|
confirmationSubText = intl.formatMessage({
|
||||||
id: 'boardSelector.confirm-link-board-subtext-with-other-channel',
|
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.',
|
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>) => {
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (event.key == 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
closeDialog()
|
closeDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,13 +198,15 @@ const BoardSelector = () => {
|
|||||||
<div className='searchResults'>
|
<div className='searchResults'>
|
||||||
{/*When there are results to show*/}
|
{/*When there are results to show*/}
|
||||||
{searchQuery && results.length > 0 &&
|
{searchQuery && results.length > 0 &&
|
||||||
results.map((result) => (<BoardSelectorItem
|
results.map((result) => (
|
||||||
key={result.id}
|
<BoardSelectorItem
|
||||||
item={result}
|
key={result.id}
|
||||||
linkBoard={linkBoard}
|
item={result}
|
||||||
unlinkBoard={unlinkBoard}
|
linkBoard={linkBoard}
|
||||||
currentChannel={currentChannel}
|
unlinkBoard={unlinkBoard}
|
||||||
/>))}
|
currentChannel={currentChannel}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
{/*when user searched for something and there were no results*/}
|
{/*when user searched for something and there were no results*/}
|
||||||
{emptyResult && <EmptyResults query={searchQuery}/>}
|
{emptyResult && <EmptyResults query={searchQuery}/>}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import BoardSelectorItem from './boardSelectorItem'
|
|||||||
describe('components/boardSelectorItem', () => {
|
describe('components/boardSelectorItem', () => {
|
||||||
it('renders board without title', async () => {
|
it('renders board without title', async () => {
|
||||||
const board = createBoard()
|
const board = createBoard()
|
||||||
board.title = ""
|
board.title = ''
|
||||||
|
|
||||||
const {container} = render(wrapIntl(
|
const {container} = render(wrapIntl(
|
||||||
<BoardSelectorItem
|
<BoardSelectorItem
|
||||||
@@ -29,7 +29,7 @@ describe('components/boardSelectorItem', () => {
|
|||||||
|
|
||||||
it('renders linked board', async () => {
|
it('renders linked board', async () => {
|
||||||
const board = createBoard()
|
const board = createBoard()
|
||||||
board.title = "Test title"
|
board.title = 'Test title'
|
||||||
|
|
||||||
const {container} = render(wrapIntl(
|
const {container} = render(wrapIntl(
|
||||||
<BoardSelectorItem
|
<BoardSelectorItem
|
||||||
@@ -44,7 +44,7 @@ describe('components/boardSelectorItem', () => {
|
|||||||
|
|
||||||
it('renders not linked board', async () => {
|
it('renders not linked board', async () => {
|
||||||
const board = createBoard()
|
const board = createBoard()
|
||||||
board.title = "Test title"
|
board.title = 'Test title'
|
||||||
|
|
||||||
const {container} = render(wrapIntl(
|
const {container} = render(wrapIntl(
|
||||||
<BoardSelectorItem
|
<BoardSelectorItem
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {useIntl, FormattedMessage} from 'react-intl'
|
import {FormattedMessage, useIntl} from 'react-intl'
|
||||||
|
|
||||||
import {Board} from 'src/blocks/board'
|
import {Board} from 'src/blocks/board'
|
||||||
import Button from 'src/widgets/buttons/button'
|
import Button from 'src/widgets/buttons/button'
|
||||||
@@ -22,6 +22,7 @@ const BoardSelectorItem = (props: Props) => {
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const untitledBoardTitle = intl.formatMessage({id: 'ViewTitle.untitled-board', defaultMessage: 'Untitled board'})
|
const untitledBoardTitle = intl.formatMessage({id: 'ViewTitle.untitled-board', defaultMessage: 'Untitled board'})
|
||||||
const resultTitle = item.title || untitledBoardTitle
|
const resultTitle = item.title || untitledBoardTitle
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='BoardSelectorItem'>
|
<div className='BoardSelectorItem'>
|
||||||
<div className='BoardSelectorItem-info'>
|
<div className='BoardSelectorItem-info'>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
waitFor,
|
waitFor,
|
||||||
within
|
within,
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
@@ -20,7 +20,7 @@ import {MemoryRouter, Router} from 'react-router-dom'
|
|||||||
|
|
||||||
import Mutator from 'src/mutator'
|
import Mutator from 'src/mutator'
|
||||||
import {Team} from 'src/store/teams'
|
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 {IUser} from 'src/user'
|
||||||
import {mockDOM, mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
import {mockDOM, mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
|||||||
|
|
||||||
const useTemplateButton = screen.getByText('Use this template').parentElement
|
const useTemplateButton = screen.getByText('Use this template').parentElement
|
||||||
expect(useTemplateButton).not.toBeNull()
|
expect(useTemplateButton).not.toBeNull()
|
||||||
|
|
||||||
await userEvent.click(useTemplateButton!)
|
await userEvent.click(useTemplateButton!)
|
||||||
|
|
||||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
||||||
@@ -313,11 +313,10 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
|||||||
expect(divBoardToSelect).not.toBeNull()
|
expect(divBoardToSelect).not.toBeNull()
|
||||||
|
|
||||||
await userEvent.click(divBoardToSelect!)
|
await userEvent.click(divBoardToSelect!)
|
||||||
|
|
||||||
|
|
||||||
const useTemplateButton = screen.getByText('Use this template').parentElement
|
const useTemplateButton = screen.getByText('Use this template').parentElement
|
||||||
expect(useTemplateButton).not.toBeNull()
|
expect(useTemplateButton).not.toBeNull()
|
||||||
|
|
||||||
await userEvent.click(useTemplateButton!)
|
await userEvent.click(useTemplateButton!)
|
||||||
|
|
||||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
||||||
@@ -339,12 +338,12 @@ describe('components/boardTemplateSelector/boardTemplateSelector', () => {
|
|||||||
expect(divBoardToSelect).not.toBeNull()
|
expect(divBoardToSelect).not.toBeNull()
|
||||||
|
|
||||||
await userEvent.click(divBoardToSelect!)
|
await userEvent.click(divBoardToSelect!)
|
||||||
|
|
||||||
const useTemplateButton = screen.getByText('Use this template').parentElement
|
const useTemplateButton = screen.getByText('Use this template').parentElement
|
||||||
expect(useTemplateButton).not.toBeNull()
|
expect(useTemplateButton).not.toBeNull()
|
||||||
|
|
||||||
await userEvent.click(useTemplateButton!)
|
await userEvent.click(useTemplateButton!)
|
||||||
|
|
||||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
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(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'}))
|
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
|
const useTemplateButton = screen.getByText('Use this template').parentElement
|
||||||
expect(useTemplateButton).not.toBeNull()
|
expect(useTemplateButton).not.toBeNull()
|
||||||
|
|
||||||
await userEvent.click(useTemplateButton!)
|
await userEvent.click(useTemplateButton!)
|
||||||
|
|
||||||
await waitFor(() => expect(mockedMutator.addBoardFromTemplate).toBeCalledTimes(1))
|
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(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'}))
|
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.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {
|
import React, {
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {FormattedMessage, useIntl} from 'react-intl'
|
import {FormattedMessage, useIntl} from 'react-intl'
|
||||||
import {useHistory, useRouteMatch} from 'react-router-dom'
|
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 Button from 'src/widgets/buttons/button'
|
||||||
import octoClient from 'src/octoClient'
|
import octoClient from 'src/octoClient'
|
||||||
import mutator from 'src/mutator'
|
import mutator from 'src/mutator'
|
||||||
import {getTemplates, getCurrentBoardId} from 'src/store/boards'
|
import {getCurrentBoardId, getTemplates} from 'src/store/boards'
|
||||||
import {getCurrentTeam, Team} from 'src/store/teams'
|
import {Team, getCurrentTeam} from 'src/store/teams'
|
||||||
import {fetchGlobalTemplates, getGlobalTemplates} from 'src/store/globalTemplates'
|
import {fetchGlobalTemplates, getGlobalTemplates} from 'src/store/globalTemplates'
|
||||||
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
||||||
import TelemetryClient, {TelemetryActions, TelemetryCategory} from 'src/telemetry/telemetryClient'
|
import TelemetryClient, {TelemetryActions, TelemetryCategory} from 'src/telemetry/telemetryClient'
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import {
|
import {
|
||||||
render,
|
|
||||||
within,
|
|
||||||
act,
|
act,
|
||||||
waitFor
|
render,
|
||||||
|
waitFor,
|
||||||
|
within,
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {MockStoreEnhanced} from 'redux-mock-store'
|
import {MockStoreEnhanced} from 'redux-mock-store'
|
||||||
import {Provider as ReduxProvider} from 'react-redux'
|
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 {mockStateStore, wrapDNDIntl} from 'src/testUtils'
|
||||||
|
|
||||||
import {IUser} from 'src/user'
|
import {IUser} from 'src/user'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 {Board} from 'src/blocks/board'
|
||||||
import {Card} from 'src/blocks/card'
|
import {Card} from 'src/blocks/card'
|
||||||
@@ -45,6 +45,7 @@ const BoardTemplateSelectorPreview = (props: Props) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isSubscribed = false
|
isSubscribed = false
|
||||||
}
|
}
|
||||||
@@ -62,6 +63,7 @@ const BoardTemplateSelectorPreview = (props: Props) => {
|
|||||||
if (!activeView) {
|
if (!activeView) {
|
||||||
return {visible: [], hidden: []}
|
return {visible: [], hidden: []}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getVisibleAndHiddenGroups(activeTemplateCards, activeView.fields.visibleOptionIds, activeView?.fields.hiddenOptionIds, groupByProperty)
|
return getVisibleAndHiddenGroups(activeTemplateCards, activeView.fields.visibleOptionIds, activeView?.fields.hiddenOptionIds, groupByProperty)
|
||||||
}, [activeTemplateCards, activeView, groupByProperty])
|
}, [activeTemplateCards, activeView, groupByProperty])
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import AddIcon from 'src/widgets/icons/add'
|
|||||||
import BoardSwitcherDialog from 'src/components/boardsSwitcherDialog/boardSwitcherDialog'
|
import BoardSwitcherDialog from 'src/components/boardsSwitcherDialog/boardSwitcherDialog'
|
||||||
import {Utils} from 'src/utils'
|
import {Utils} from 'src/utils'
|
||||||
import {Constants} from 'src/constants'
|
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 IconButton from 'src/widgets/buttons/iconButton'
|
||||||
import SearchForBoardsTourStep from 'src/components/onboardingTour/searchForBoards/searchForBoards'
|
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 {render} from '@testing-library/react'
|
||||||
|
|
||||||
import {createMemoryHistory, History} from 'history'
|
import {History, createMemoryHistory} from 'history'
|
||||||
|
|
||||||
import {Router} from 'react-router-dom'
|
import {Router} from 'react-router-dom'
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {
|
import React, {
|
||||||
|
MutableRefObject,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
useRef,
|
|
||||||
createRef,
|
createRef,
|
||||||
useState,
|
|
||||||
useEffect,
|
useEffect,
|
||||||
MutableRefObject
|
useRef,
|
||||||
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
|
|
||||||
import './boardSwitcherDialog.scss'
|
import './boardSwitcherDialog.scss'
|
||||||
@@ -19,7 +19,7 @@ import SearchDialog from 'src/components/searchDialog/searchDialog'
|
|||||||
import Globe from 'src/widgets/icons/globe'
|
import Globe from 'src/widgets/icons/globe'
|
||||||
import LockOutline from 'src/widgets/icons/lockOutline'
|
import LockOutline from 'src/widgets/icons/lockOutline'
|
||||||
import {useAppSelector} from 'src/store/hooks'
|
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 {getMe} from 'src/store/users'
|
||||||
import {Utils} from 'src/utils'
|
import {Utils} from 'src/utils'
|
||||||
import {BoardTypeOpen, BoardTypePrivate} from 'src/blocks/board'
|
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'})
|
const untitledBoardTitle = intl.formatMessage({id: 'ViewTitle.untitled-board', defaultMessage: 'Untitled board'})
|
||||||
refs.current = items.map((_, i) => refs.current[i] ?? createRef())
|
refs.current = items.map((_, i) => refs.current[i] ?? createRef())
|
||||||
setRefs(refs)
|
setRefs(refs)
|
||||||
|
|
||||||
return items.map((item, i) => {
|
return items.map((item, i) => {
|
||||||
const resultTitle = item.title || untitledBoardTitle
|
const resultTitle = item.title || untitledBoardTitle
|
||||||
const teamTitle = teamsById[item.teamId].title
|
const teamTitle = teamsById[item.teamId].title
|
||||||
@@ -83,6 +84,7 @@ const BoardSwitcherDialog = (props: Props): JSX.Element => {
|
|||||||
...prevIDs,
|
...prevIDs,
|
||||||
[i]: [item.teamId, item.id],
|
[i]: [item.teamId, item.id],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ describe('components/boardsUnfurl/BoardsUnfurl', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const board = {...createBoard(), title: 'test board'}
|
const board = {...createBoard(), title: 'test board'}
|
||||||
|
|
||||||
// mockedOctoClient.getBoard.mockResolvedValueOnce(board)
|
// mockedOctoClient.getBoard.mockResolvedValueOnce(board)
|
||||||
|
|
||||||
const component = (
|
const component = (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useState, useEffect} from 'react'
|
import React, {useEffect, useState} from 'react'
|
||||||
import {IntlProvider, FormattedMessage, useIntl} from 'react-intl'
|
import {FormattedMessage, IntlProvider, useIntl} from 'react-intl'
|
||||||
|
|
||||||
import WithWebSockets from 'src/components/withWebSockets'
|
import WithWebSockets from 'src/components/withWebSockets'
|
||||||
import {useWebsockets} from 'src/hooks/websockets'
|
import {useWebsockets} from 'src/hooks/websockets'
|
||||||
@@ -10,7 +10,7 @@ import {getLanguage} from 'src/store/language'
|
|||||||
import {useAppSelector} from 'src/store/hooks'
|
import {useAppSelector} from 'src/store/hooks'
|
||||||
import {getCurrentTeamId} from 'src/store/teams'
|
import {getCurrentTeamId} from 'src/store/teams'
|
||||||
|
|
||||||
import {WSClient, MMWebSocketClient} from 'src/wsclient'
|
import {MMWebSocketClient, WSClient} from 'src/wsclient'
|
||||||
import manifest from 'src/manifest'
|
import manifest from 'src/manifest'
|
||||||
|
|
||||||
import {getMessages} from 'src/i18n'
|
import {getMessages} from 'src/i18n'
|
||||||
@@ -86,6 +86,7 @@ export const BoardsUnfurl = (props: Props): JSX.Element => {
|
|||||||
const [firstCard] = cards as Card[]
|
const [firstCard] = cards as Card[]
|
||||||
if (!firstCard || !fetchedBoard || firstCard.type !== 'card') {
|
if (!firstCard || !fetchedBoard || firstCard.type !== 'card') {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
setCard(firstCard)
|
setCard(firstCard)
|
||||||
@@ -102,12 +103,14 @@ export const BoardsUnfurl = (props: Props): JSX.Element => {
|
|||||||
const [firstContentBlock] = contentBlock
|
const [firstContentBlock] = contentBlock
|
||||||
if (!firstContentBlock) {
|
if (!firstContentBlock) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
setContent(firstContentBlock)
|
setContent(firstContentBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
fetchData()
|
fetchData()
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ function fixTimestampToMinutesAccuracy(timestamp: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cardsWithValue(cards: readonly Card[], property: IPropertyTemplate): Card[] {
|
function cardsWithValue(cards: readonly Card[], property: IPropertyTemplate): Card[] {
|
||||||
return cards.
|
return cards
|
||||||
filter((card) => Boolean(getCardProperty(card, property)))
|
.filter((card) => Boolean(getCardProperty(card, property)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// 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) {
|
if (cards.length === 0) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return String((((cards.length - cardsWithValue(cards, property).length) / cards.length) * 100).toFixed(0)) + '%'
|
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) {
|
if (cards.length === 0) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return String(((cardsWithValue(cards, property).length / cards.length) * 100).toFixed(0)) + '%'
|
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
|
let values = 0
|
||||||
|
|
||||||
if (property.type === 'multiSelect') {
|
if (property.type === 'multiSelect') {
|
||||||
cardsWithValue(cards, property).
|
cardsWithValue(cards, property)
|
||||||
forEach((card) => {
|
.forEach((card) => {
|
||||||
values += (getCardProperty(card, property) as string[]).length
|
values += (getCardProperty(card, property) as string[]).length
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -140,8 +142,8 @@ function countUniqueValue(cards: readonly Card[], property: IPropertyTemplate):
|
|||||||
function sum(cards: readonly Card[], property: IPropertyTemplate): string {
|
function sum(cards: readonly Card[], property: IPropertyTemplate): string {
|
||||||
let result = 0
|
let result = 0
|
||||||
|
|
||||||
cardsWithValue(cards, property).
|
cardsWithValue(cards, property)
|
||||||
forEach((card) => {
|
.forEach((card) => {
|
||||||
result += parseFloat(getCardProperty(card, property) as string)
|
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 result = parseFloat(sum(cards, property))
|
||||||
const avg = result / numCards
|
const avg = result / numCards
|
||||||
|
|
||||||
return String(Utils.roundTo(avg, ROUNDED_DECIMAL_PLACES))
|
return String(Utils.roundTo(avg, ROUNDED_DECIMAL_PLACES))
|
||||||
}
|
}
|
||||||
|
|
||||||
function median(cards: readonly Card[], property: IPropertyTemplate): string {
|
function median(cards: readonly Card[], property: IPropertyTemplate): string {
|
||||||
const sorted = cardsWithValue(cards, property).
|
const sorted = cardsWithValue(cards, property)
|
||||||
sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (!getCardProperty(a, property)) {
|
if (!getCardProperty(a, property)) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -231,6 +234,7 @@ function earliest(cards: readonly Card[], property: IPropertyTemplate, intl: Int
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
const date = new Date(result)
|
const date = new Date(result)
|
||||||
|
|
||||||
return property.type === 'date' ? Utils.displayDate(date, intl) : Utils.displayDateTime(date, intl)
|
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)
|
result = Math.min(result, timestamp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +256,7 @@ function latest(cards: readonly Card[], property: IPropertyTemplate, intl: IntlS
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
const date = new Date(result)
|
const date = new Date(result)
|
||||||
|
|
||||||
return property.type === 'date' ? Utils.displayDate(date, intl) : Utils.displayDateTime(date, intl)
|
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)
|
result = Math.max(result, timestamp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,10 +283,12 @@ function getTimestampsFromPropertyValue(value: number | string | string[]): numb
|
|||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return [property.from, property.to].flatMap((e) => {
|
return [property.from, property.to].flatMap((e) => {
|
||||||
return e ? [e] : []
|
return e ? [e] : []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +301,7 @@ function dateRange(cards: readonly Card[], property: IPropertyTemplate, intl: In
|
|||||||
if (resultLatest === Number.NEGATIVE_INFINITY) {
|
if (resultLatest === Number.NEGATIVE_INFINITY) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return moment.duration(resultLatest - resultEarliest, 'milliseconds').locale(intl.locale.toLowerCase()).humanize()
|
return moment.duration(resultLatest - resultEarliest, 'milliseconds').locale(intl.locale.toLowerCase()).humanize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import Select, {components, DropdownIndicatorProps, StylesConfig} from 'react-select'
|
import Select, {DropdownIndicatorProps, StylesConfig, components} from 'react-select'
|
||||||
|
|
||||||
|
import {IntlShape, useIntl} from 'react-intl'
|
||||||
import {useIntl, IntlShape} from 'react-intl'
|
|
||||||
|
|
||||||
import {getSelectBaseStyle} from 'src/theme'
|
import {getSelectBaseStyle} from 'src/theme'
|
||||||
import ChevronUp from 'src/widgets/icons/chevronUp'
|
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 {Provider as ReduxProvider} from 'react-redux'
|
||||||
|
|
||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
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 {IPropertyTemplate} from 'src/blocks/board'
|
||||||
|
|
||||||
import CalendarView from './fullCalendar'
|
import CalendarView from './fullCalendar'
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import React, {useCallback, useMemo, useState} from 'react'
|
|||||||
import {useIntl} from 'react-intl'
|
import {useIntl} from 'react-intl'
|
||||||
|
|
||||||
import FullCalendar, {
|
import FullCalendar, {
|
||||||
|
DayCellContentArg,
|
||||||
EventChangeArg,
|
EventChangeArg,
|
||||||
EventInput,
|
|
||||||
EventContentArg,
|
EventContentArg,
|
||||||
DayCellContentArg
|
EventInput,
|
||||||
} from '@fullcalendar/react'
|
} from '@fullcalendar/react'
|
||||||
|
|
||||||
import interactionPlugin from '@fullcalendar/interaction'
|
import interactionPlugin from '@fullcalendar/interaction'
|
||||||
@@ -60,6 +60,7 @@ function createDatePropertyFromCalendarDates(start: Date, end: Date): DateProper
|
|||||||
if (dateTo !== dateFrom) {
|
if (dateTo !== dateFrom) {
|
||||||
dateProperty.to = dateTo
|
dateProperty.to = dateTo
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateProperty
|
return dateProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ function createDatePropertyFromCalendarDate(start: Date): DateProperty {
|
|||||||
const dateFrom = start.getTime() - timeZoneOffset(start.getTime())
|
const dateFrom = start.getTime() - timeZoneOffset(start.getTime())
|
||||||
|
|
||||||
const dateProperty: DateProperty = {from: dateFrom}
|
const dateProperty: DateProperty = {from: dateFrom}
|
||||||
|
|
||||||
return dateProperty
|
return dateProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +99,7 @@ const CalendarFullView = (props: Props): JSX.Element|null => {
|
|||||||
if (readonly || !dateDisplayProperty || propsRegistry.get(dateDisplayProperty.type).isReadOnly) {
|
if (readonly || !dateDisplayProperty || propsRegistry.get(dateDisplayProperty.type).isReadOnly) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}, [readonly, dateDisplayProperty])
|
}, [readonly, dateDisplayProperty])
|
||||||
|
|
||||||
@@ -118,6 +121,7 @@ const CalendarFullView = (props: Props): JSX.Element|null => {
|
|||||||
//full calendar end date is exclusive, so increment by 1 day.
|
//full calendar end date is exclusive, so increment by 1 day.
|
||||||
dateTo.setDate(dateTo.getDate() + 1)
|
dateTo.setDate(dateTo.getDate() + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
id: card.id,
|
id: card.id,
|
||||||
title: card.title,
|
title: card.title,
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ const calculateBadges = (contents: ContentsType, comments: CommentBlock[]): Badg
|
|||||||
updateCounters(content)
|
updateCounters(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
description: text > 0,
|
description: text > 0,
|
||||||
comments: comments.length,
|
comments: comments.length,
|
||||||
@@ -84,6 +85,7 @@ const CardBadges = (props: Props) => {
|
|||||||
}
|
}
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const {checkboxes} = badges
|
const {checkboxes} = badges
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`CardBadges ${className || ''}`}>
|
<div className={`CardBadges ${className || ''}`}>
|
||||||
{badges.description &&
|
{badges.description &&
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {
|
import React, {
|
||||||
|
Fragment,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
Fragment,
|
|
||||||
useMemo
|
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {FormattedMessage, useIntl, IntlShape} from 'react-intl'
|
import {FormattedMessage, IntlShape, useIntl} from 'react-intl'
|
||||||
|
|
||||||
import {BlockIcons} from 'src/blockIcons'
|
import {BlockIcons} from 'src/blockIcons'
|
||||||
import {Card} from 'src/blocks/card'
|
import {Card} from 'src/blocks/card'
|
||||||
@@ -30,7 +30,7 @@ import TelemetryClient, {TelemetryActions, TelemetryCategory} from 'src/telemetr
|
|||||||
import BlockIconSelector from 'src/components/blockIconSelector'
|
import BlockIconSelector from 'src/components/blockIconSelector'
|
||||||
|
|
||||||
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
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 {updateContents} from 'src/store/contents'
|
||||||
import {Permission} from 'src/constants'
|
import {Permission} from 'src/constants'
|
||||||
import {useHasCurrentBoardPermissions} from 'src/hooks/permissions'
|
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)
|
const newBlock = await mutator.insertBlock(block.boardId, block, description, afterRedo, beforeUndo)
|
||||||
dispatch(updateContents([newBlock]))
|
dispatch(updateContents([newBlock]))
|
||||||
|
|
||||||
return newBlock
|
return newBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
saveTitleRef.current && saveTitleRef.current()
|
saveTitleRef.current?.()
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -339,6 +340,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
|||||||
} else {
|
} else {
|
||||||
newBlock = await addBlockNewEditor(card, intl, block.value, {}, block.contentType, afterBlock?.id, dispatch)
|
newBlock = await addBlockNewEditor(card, intl, block.value, {}, block.contentType, afterBlock?.id, dispatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {...block, id: newBlock.id}
|
return {...block, id: newBlock.id}
|
||||||
}}
|
}}
|
||||||
onBlockModified={async (block: any): Promise<BlockData<any>|null> => {
|
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'})
|
const description = intl.formatMessage({id: 'ContentBlock.DeleteAction', defaultMessage: 'delete'})
|
||||||
|
|
||||||
mutator.deleteBlock(originalContentBlock, description)
|
mutator.deleteBlock(originalContentBlock, description)
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const newBlock = {
|
const newBlock = {
|
||||||
@@ -363,6 +366,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
|||||||
newBlock.fields = {...newBlock.fields, value: block.value.checked}
|
newBlock.fields = {...newBlock.fields, value: block.value.checked}
|
||||||
}
|
}
|
||||||
mutator.updateBlock(card.boardId, newBlock, originalContentBlock, intl.formatMessage({id: 'ContentBlock.editCardText', defaultMessage: 'edit card text'}))
|
mutator.updateBlock(card.boardId, newBlock, originalContentBlock, intl.formatMessage({id: 'ContentBlock.editCardText', defaultMessage: 'edit card text'}))
|
||||||
|
|
||||||
return block
|
return block
|
||||||
}}
|
}}
|
||||||
onBlockMoved={async (block: BlockData, beforeBlock: BlockData|null, afterBlock: BlockData|null): Promise<void> => {
|
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'
|
let sourceWhere: 'after'|'before'
|
||||||
if (idx === -1) {
|
if (idx === -1) {
|
||||||
Utils.logError('Unable to find the block id in the order of the current block')
|
Utils.logError('Unable to find the block id in the order of the current block')
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (idx === 0) {
|
if (idx === 0) {
|
||||||
@@ -383,6 +388,7 @@ const CardDetail = (props: Props): JSX.Element|null => {
|
|||||||
}
|
}
|
||||||
if (afterBlock && afterBlock.id) {
|
if (afterBlock && afterBlock.id) {
|
||||||
await mutator.moveContentBlock(block.id, afterBlock.id, 'after', sourceBlockId, sourceWhere, intl.formatMessage({id: 'ContentBlock.moveBlock', defaultMessage: 'move card content'}))
|
await mutator.moveContentBlock(block.id, afterBlock.id, 'after', sourceBlockId, sourceWhere, intl.formatMessage({id: 'ContentBlock.moveBlock', defaultMessage: 'move card content'}))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (beforeBlock && beforeBlock.id) {
|
if (beforeBlock && beforeBlock.id) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {act} from 'react-dom/test-utils'
|
|||||||
|
|
||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
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 CardDetailContents from './cardDetailContents'
|
||||||
import {CardDetailProvider} from './cardDetailContext'
|
import {CardDetailProvider} from './cardDetailContext'
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React from 'react'
|
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 {Card} from 'src/blocks/card'
|
||||||
import {createTextBlock} from 'src/blocks/textBlock'
|
import {createTextBlock} from 'src/blocks/textBlock'
|
||||||
import {Block} from 'src/blocks/block'
|
import {Block} from 'src/blocks/block'
|
||||||
@@ -182,6 +182,7 @@ const CardDetailContents = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='octo-content CardDetailContents'>
|
<div className='octo-content CardDetailContents'>
|
||||||
<div className='octo-block'>
|
<div className='octo-block'>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {Provider as ReduxProvider} from 'react-redux'
|
|||||||
|
|
||||||
import userEvent from '@testing-library/user-event'
|
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'
|
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ function addContentMenu(intl: IntlShape, type: BlockTypes): JSX.Element {
|
|||||||
const handler = contentRegistry.getHandler(type)
|
const handler = contentRegistry.getHandler(type)
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
Utils.logError(`addContentMenu, unknown content type: ${type}`)
|
Utils.logError(`addContentMenu, unknown content type: ${type}`)
|
||||||
|
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
const cardDetail = useCardDetailContext()
|
const cardDetail = useCardDetailContext()
|
||||||
@@ -39,6 +40,7 @@ function addContentMenu(intl: IntlShape, type: BlockTypes): JSX.Element {
|
|||||||
|
|
||||||
const CardDetailContentsMenu = () => {
|
const CardDetailContentsMenu = () => {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='CardDetailContentsMenu content add-content'>
|
<div className='CardDetailContentsMenu content add-content'>
|
||||||
<MenuWrapper>
|
<MenuWrapper>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {
|
import React, {
|
||||||
createContext,
|
|
||||||
ReactElement,
|
ReactElement,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
useCallback
|
|
||||||
} from 'react'
|
} from 'react'
|
||||||
|
|
||||||
import {useIntl} from 'react-intl'
|
import {useIntl} from 'react-intl'
|
||||||
@@ -37,6 +37,7 @@ export function useCardDetailContext(): CardDetailContextType {
|
|||||||
if (!cardDetailContext) {
|
if (!cardDetailContext) {
|
||||||
throw new Error('CardDetailContext is not available!')
|
throw new Error('CardDetailContext is not available!')
|
||||||
}
|
}
|
||||||
|
|
||||||
return cardDetailContext
|
return cardDetailContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
|
act,
|
||||||
|
fireEvent,
|
||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
act,
|
|
||||||
fireEvent
|
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import {mocked} from 'jest-mock'
|
import {mocked} from 'jest-mock'
|
||||||
@@ -206,7 +206,7 @@ describe('components/cardDetail/CardDetailProperties', () => {
|
|||||||
await act(() => userEvent.click(menuElement))
|
await act(() => userEvent.click(menuElement))
|
||||||
const numberType = screen.getByRole('button', {name: /number/i})
|
const numberType = screen.getByRole('button', {name: /number/i})
|
||||||
|
|
||||||
await act( () => userEvent.click(numberType))
|
await act(() => userEvent.click(numberType))
|
||||||
|
|
||||||
expect(mockedMutator.insertPropertyTemplate).toHaveBeenCalledTimes(1)
|
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 only the name has changed, set the property without warning
|
||||||
if (affectsNumOfCards === '0' || oldType === newType) {
|
if (affectsNumOfCards === '0' || oldType === newType) {
|
||||||
mutator.changePropertyTypeAndName(board, cards, propertyTemplate, newType.type, newName)
|
mutator.changePropertyTypeAndName(board, cards, propertyTemplate, newType.type, newName)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import moment from 'moment'
|
|||||||
|
|
||||||
import {mocked} from 'jest-mock'
|
import {mocked} from 'jest-mock'
|
||||||
|
|
||||||
import {wrapIntl, mockStateStore} from 'src/testUtils'
|
import {mockStateStore, wrapIntl} from 'src/testUtils'
|
||||||
|
|
||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
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
|
// Only modify _own_ comments, EXCEPT for Admins, which can delete _any_ comment
|
||||||
// NOTE: editing comments will exist in the future (in addition to deleting)
|
// NOTE: editing comments will exist in the future (in addition to deleting)
|
||||||
const canDeleteComment: boolean = canDeleteOthersComments || me?.id === comment.modifiedBy
|
const canDeleteComment: boolean = canDeleteOthersComments || me?.id === comment.modifiedBy
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comment
|
<Comment
|
||||||
key={comment.id}
|
key={comment.id}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {useEffect, useCallback} from 'react'
|
import {useCallback, useEffect} from 'react'
|
||||||
import {useIntl} from 'react-intl'
|
import {useIntl} from 'react-intl'
|
||||||
|
|
||||||
import {ImageBlock, createImageBlock} from 'src/blocks/imageBlock'
|
import {ImageBlock, createImageBlock} from 'src/blocks/imageBlock'
|
||||||
@@ -77,6 +77,7 @@ export default function useImagePaste(boardId: string, cardId: string, contentOr
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener('paste', onPaste)
|
document.addEventListener('paste', onPaste)
|
||||||
document.addEventListener('drop', onDrop)
|
document.addEventListener('drop', onDrop)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('paste', onPaste)
|
document.removeEventListener('paste', onPaste)
|
||||||
document.removeEventListener('drop', onDrop)
|
document.removeEventListener('drop', onDrop)
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ describe('components/cardDialog', () => {
|
|||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
test('should match snapshot', async () => {
|
test('should match snapshot', async () => {
|
||||||
|
|
||||||
const {container} = render(wrapDNDIntl(
|
const {container} = render(wrapDNDIntl(
|
||||||
<ReduxProvider store={store}>
|
<ReduxProvider store={store}>
|
||||||
<CardDialog
|
<CardDialog
|
||||||
@@ -324,7 +323,7 @@ describe('components/cardDialog', () => {
|
|||||||
/>
|
/>
|
||||||
</ReduxProvider>,
|
</ReduxProvider>,
|
||||||
))
|
))
|
||||||
|
|
||||||
expect(container).toMatchSnapshot()
|
expect(container).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// 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 {FormattedMessage, useIntl} from 'react-intl'
|
||||||
|
|
||||||
import {Board} from 'src/blocks/board'
|
import {Board} from 'src/blocks/board'
|
||||||
@@ -67,6 +67,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
|||||||
const makeTemplateClicked = async () => {
|
const makeTemplateClicked = async () => {
|
||||||
if (!card) {
|
if (!card) {
|
||||||
Utils.assertFailure('card')
|
Utils.assertFailure('card')
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +90,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
|||||||
const handleDeleteCard = async () => {
|
const handleDeleteCard = async () => {
|
||||||
if (!card) {
|
if (!card) {
|
||||||
Utils.assertFailure()
|
Utils.assertFailure()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.DeleteCard, {board: props.board.id, view: props.activeView.id, card: card.id})
|
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
|
// so adding des
|
||||||
if (card?.title === '' && card?.fields.contentOrder.length === 0) {
|
if (card?.title === '' && card?.fields.contentOrder.length === 0) {
|
||||||
handleDeleteCard()
|
handleDeleteCard()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,6 +269,7 @@ const CardDialog = (props: Props): JSX.Element => {
|
|||||||
if (!isTemplate && !card?.limited) {
|
if (!isTemplate && !card?.limited) {
|
||||||
return (<>{attachBtn()}{following ? unfollowBtn : followBtn}</>)
|
return (<>{attachBtn()}{following ? unfollowBtn : followBtn}</>)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<>{attachBtn()}</>)
|
return (<>{attachBtn()}</>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useCallback, useEffect, useState} from 'react'
|
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 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 {IUser, UserConfigPatch} from 'src/user'
|
||||||
import {
|
import {
|
||||||
|
getCardHiddenWarningSnoozeUntil,
|
||||||
|
getCardLimitSnoozeUntil,
|
||||||
getMe,
|
getMe,
|
||||||
patchProps,
|
patchProps,
|
||||||
getCardLimitSnoozeUntil,
|
|
||||||
getCardHiddenWarningSnoozeUntil
|
|
||||||
} from 'src/store/users'
|
} 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 TelemetryClient, {TelemetryActions, TelemetryCategory} from 'src/telemetry/telemetryClient'
|
||||||
import CheckIcon from 'src/widgets/icons/check'
|
import CheckIcon from 'src/widgets/icons/check'
|
||||||
import NotificationBox from 'src/widgets/notificationBox/notificationBox'
|
import NotificationBox from 'src/widgets/notificationBox/notificationBox'
|
||||||
@@ -103,10 +103,12 @@ const CardLimitNotification = (props: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!show) {
|
if (!show) {
|
||||||
const interval = setInterval(() => setTime(Date.now()), checkSnoozeInterval)
|
const interval = setInterval(() => setTime(Date.now()), checkSnoozeInterval)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => null
|
return () => null
|
||||||
}, [show])
|
}, [show])
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
fireEvent,
|
fireEvent,
|
||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
within
|
within,
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
@@ -375,7 +375,7 @@ describe('components/centerPanel', () => {
|
|||||||
))
|
))
|
||||||
const cardElement = screen.getByRole('textbox', {name: 'card1'})
|
const cardElement = screen.getByRole('textbox', {name: 'card1'})
|
||||||
expect(cardElement).not.toBeNull()
|
expect(cardElement).not.toBeNull()
|
||||||
fireEvent.click(cardElement, {shiftKey: true})
|
fireEvent.click(cardElement, {shiftKey: true})
|
||||||
expect(container).toMatchSnapshot()
|
expect(container).toMatchSnapshot()
|
||||||
|
|
||||||
//delete
|
//delete
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
/* eslint-disable max-lines */
|
|
||||||
import React, {
|
import React, {
|
||||||
useState,
|
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo
|
useMemo,
|
||||||
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {useIntl} from 'react-intl'
|
import {useIntl} from 'react-intl'
|
||||||
import {useHotkeys} from 'react-hotkeys-hook'
|
import {useHotkeys} from 'react-hotkeys-hook'
|
||||||
@@ -15,32 +15,32 @@ import {ClientConfig} from 'src/config/clientConfig'
|
|||||||
import {Block} from 'src/blocks/block'
|
import {Block} from 'src/blocks/block'
|
||||||
import {BlockIcons} from 'src/blockIcons'
|
import {BlockIcons} from 'src/blockIcons'
|
||||||
import {Card, createCard} from 'src/blocks/card'
|
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 {BoardView} from 'src/blocks/boardView'
|
||||||
import {CardFilter} from 'src/cardFilter'
|
import {CardFilter} from 'src/cardFilter'
|
||||||
import mutator from 'src/mutator'
|
import mutator from 'src/mutator'
|
||||||
import {Utils} from 'src/utils'
|
import {Utils} from 'src/utils'
|
||||||
import {UserSettings} from 'src/userSettings'
|
import {UserSettings} from 'src/userSettings'
|
||||||
import {
|
import {
|
||||||
getCurrentCard,
|
|
||||||
addCard as addCardAction,
|
addCard as addCardAction,
|
||||||
addTemplate as addTemplateAction,
|
addTemplate as addTemplateAction,
|
||||||
showCardHiddenWarning
|
getCurrentCard,
|
||||||
|
showCardHiddenWarning,
|
||||||
} from 'src/store/cards'
|
} from 'src/store/cards'
|
||||||
import {getCardLimitTimestamp} from 'src/store/limits'
|
import {getCardLimitTimestamp} from 'src/store/limits'
|
||||||
import {updateView} from 'src/store/views'
|
import {updateView} from 'src/store/views'
|
||||||
import {getVisibleAndHiddenGroups} from 'src/boardUtils'
|
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 {getClientConfig} from 'src/store/clientConfig'
|
||||||
|
|
||||||
import './centerPanel.scss'
|
import './centerPanel.scss'
|
||||||
|
|
||||||
import {useAppSelector, useAppDispatch} from 'src/store/hooks'
|
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getMe,
|
|
||||||
getBoardUsers,
|
getBoardUsers,
|
||||||
|
getMe,
|
||||||
getOnboardingTourCategory,
|
getOnboardingTourCategory,
|
||||||
getOnboardingTourStarted,
|
getOnboardingTourStarted,
|
||||||
getOnboardingTourStep,
|
getOnboardingTourStep,
|
||||||
@@ -72,7 +72,7 @@ import {
|
|||||||
BoardTourSteps,
|
BoardTourSteps,
|
||||||
FINISHED,
|
FINISHED,
|
||||||
TOUR_BOARD,
|
TOUR_BOARD,
|
||||||
TOUR_CARD
|
TOUR_CARD,
|
||||||
} from './onboardingTour'
|
} from './onboardingTour'
|
||||||
import ShareBoardTourStep from './onboardingTour/shareBoard/shareBoard'
|
import ShareBoardTourStep from './onboardingTour/shareBoard/shareBoard'
|
||||||
|
|
||||||
@@ -393,6 +393,7 @@ const CenterPanel = (props: Props) => {
|
|||||||
defaultMessage: 'No {propertyName}',
|
defaultMessage: 'No {propertyName}',
|
||||||
}, {propertyName: groupByProperty?.name})
|
}, {propertyName: groupByProperty?.name})
|
||||||
}
|
}
|
||||||
|
|
||||||
return intl.formatMessage({id: 'centerPanel.unknown-user', defaultMessage: 'Unknown user'})
|
return intl.formatMessage({id: 'centerPanel.unknown-user', defaultMessage: 'Unknown user'})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,6 +409,7 @@ const CenterPanel = (props: Props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {visible: vg, hidden: hg}
|
return {visible: vg, hidden: hg}
|
||||||
}, [cards, activeView.fields.visibleOptionIds, activeView.fields.hiddenOptionIds, groupByProperty, boardUsers])
|
}, [cards, activeView.fields.visibleOptionIds, activeView.fields.hiddenOptionIds, groupByProperty, boardUsers])
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useState, useRef} from 'react'
|
import React, {useRef, useState} from 'react'
|
||||||
import Select from 'react-select'
|
import Select from 'react-select'
|
||||||
import {useIntl, FormattedMessage} from 'react-intl'
|
import {FormattedMessage, useIntl} from 'react-intl'
|
||||||
|
|
||||||
import {MemberRole} from 'src/blocks/board'
|
import {MemberRole} from 'src/blocks/board'
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const ArchivedFile = (props: Props): JSX.Element => {
|
|||||||
const fileExtension = useCallback(() => {
|
const fileExtension = useCallback(() => {
|
||||||
let extension = props.fileInfo.extension
|
let extension = props.fileInfo.extension
|
||||||
extension = extension?.startsWith('.') ? extension?.substring(1) : extension
|
extension = extension?.startsWith('.') ? extension?.substring(1) : extension
|
||||||
|
|
||||||
return extension?.toUpperCase()
|
return extension?.toUpperCase()
|
||||||
}, [props.fileInfo.extension])
|
}, [props.fileInfo.extension])
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const AttachmentElement = (props: Props): JSX.Element|null => {
|
|||||||
name: block.title,
|
name: block.title,
|
||||||
extension: block.title.split('.').slice(0, -1).join('.'),
|
extension: block.title.split('.').slice(0, -1).join('.'),
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const attachmentInfo = await octoClient.getFileInfo(block.boardId, block.fields.fileId)
|
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) {
|
if (fName.length > 18) {
|
||||||
let result = fName.slice(0, 15)
|
let result = fName.slice(0, 15)
|
||||||
result += '...'
|
result += '...'
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
return fName
|
return fName
|
||||||
}
|
}
|
||||||
setFileName(generateFileName(fileInfo.name))
|
setFileName(generateFileName(fileInfo.name))
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
fireEvent,
|
fireEvent,
|
||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
waitFor
|
waitFor,
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
import {mocked} from 'jest-mock'
|
import {mocked} from 'jest-mock'
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
@@ -174,7 +174,7 @@ describe('components/content/checkboxElement', () => {
|
|||||||
))
|
))
|
||||||
const input = screen.getByRole('textbox')
|
const input = screen.getByRole('textbox')
|
||||||
|
|
||||||
// should delete if title is empty
|
// should delete if title is empty
|
||||||
await userEvent.type(input, '{Escape}')
|
await userEvent.type(input, '{Escape}')
|
||||||
expect(deleteElement).toHaveBeenCalledTimes(1)
|
expect(deleteElement).toHaveBeenCalledTimes(1)
|
||||||
await userEvent.type(input, '{Enter}')
|
await userEvent.type(input, '{Enter}')
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ const CheckboxElement = (props: Props) => {
|
|||||||
const {lastAddedBlock} = cardDetail
|
const {lastAddedBlock} = cardDetail
|
||||||
if (title === '' && block.id === lastAddedBlock.id && lastAddedBlock.autoAdded && props.onDeleteElement) {
|
if (title === '' && block.id === lastAddedBlock.id && lastAddedBlock.autoAdded && props.onDeleteElement) {
|
||||||
props.onDeleteElement()
|
props.onDeleteElement()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +78,7 @@ const CheckboxElement = (props: Props) => {
|
|||||||
// Wait for the change to happen
|
// Wait for the change to happen
|
||||||
setTimeout(props.onAddElement, 100)
|
setTimeout(props.onAddElement, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {ReactElement, ReactNode} from 'react'
|
import React, {ReactElement, ReactNode} from 'react'
|
||||||
|
|
||||||
|
|
||||||
import {render} from '@testing-library/react'
|
import {render} from '@testing-library/react'
|
||||||
|
|
||||||
import {wrapIntl} from 'src/testUtils'
|
import {wrapIntl} from 'src/testUtils'
|
||||||
@@ -53,7 +52,7 @@ describe('components/content/contentElement', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should return null for unknown type', () => {
|
it('should return null for unknown type', () => {
|
||||||
jest.spyOn(console, 'error').mockImplementation()
|
jest.spyOn(console, 'error').mockImplementation()
|
||||||
|
|
||||||
const block: ContentBlock = {...contentBlock, type: 'unknown'}
|
const block: ContentBlock = {...contentBlock, type: 'unknown'}
|
||||||
const {container} = render(wrap(
|
const {container} = render(wrap(
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default function ContentElement(props: Props): JSX.Element|null {
|
|||||||
const handler = contentRegistry.getHandler(block.type)
|
const handler = contentRegistry.getHandler(block.type)
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
Utils.logError(`ContentElement, unknown content type: ${block.type}`)
|
Utils.logError(`ContentElement, unknown content type: ${block.type}`)
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
/* eslint-disable react/require-optimization */
|
|
||||||
import {IntlShape} from 'react-intl'
|
import {IntlShape} from 'react-intl'
|
||||||
|
|
||||||
import {BlockTypes} from 'src/blocks/block'
|
import {BlockTypes} from 'src/blocks/block'
|
||||||
@@ -25,6 +25,7 @@ class ContentRegistry {
|
|||||||
registerContentType(entry: ContentHandler) {
|
registerContentType(entry: ContentHandler) {
|
||||||
if (this.isContentType(entry.type)) {
|
if (this.isContentType(entry.type)) {
|
||||||
Utils.logError(`registerContentType, already registered type: ${entry.type}`)
|
Utils.logError(`registerContentType, already registered type: ${entry.type}`)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.registry.set(entry.type, entry)
|
this.registry.set(entry.type, entry)
|
||||||
|
|||||||
@@ -2,15 +2,14 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
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 {Provider as ReduxProvider} from 'react-redux'
|
||||||
|
|
||||||
|
|
||||||
import {mocked} from 'jest-mock'
|
import {mocked} from 'jest-mock'
|
||||||
|
|
||||||
import {TextBlock} from 'src/blocks/textBlock'
|
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'
|
import {Utils} from 'src/utils'
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import {act, render, screen} from '@testing-library/react'
|
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 {mocked} from 'jest-mock'
|
||||||
import {Provider as ReduxProvider} from 'react-redux'
|
import {Provider as ReduxProvider} from 'react-redux'
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ import userEvent from '@testing-library/user-event'
|
|||||||
|
|
||||||
import {Utils} from 'src/utils'
|
import {Utils} from 'src/utils'
|
||||||
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
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'
|
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 userEvent from '@testing-library/user-event'
|
||||||
|
|
||||||
import {mockStateStore} from 'src/testUtils'
|
import {mockStateStore, wrapIntl} from 'src/testUtils'
|
||||||
import {wrapIntl} from 'src/testUtils'
|
|
||||||
|
|
||||||
import CreateBoardFromTemplate from './createBoardFromTemplate'
|
import CreateBoardFromTemplate from './createBoardFromTemplate'
|
||||||
|
|
||||||
jest.mock('src/hooks/useGetAllTemplates', () => ({
|
jest.mock('src/hooks/useGetAllTemplates', () => ({
|
||||||
useGetAllTemplates: () => [{id: 'id', title: 'title', description: 'description', icon: '🍔'}]
|
useGetAllTemplates: () => [{id: 'id', title: 'title', description: 'description', icon: '🍔'}],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
describe('components/createBoardFromTemplate', () => {
|
describe('components/createBoardFromTemplate', () => {
|
||||||
@@ -26,7 +25,7 @@ describe('components/createBoardFromTemplate', () => {
|
|||||||
const store = mockStateStore([], state)
|
const store = mockStateStore([], state)
|
||||||
const setCanCreate = jest.fn
|
const setCanCreate = jest.fn
|
||||||
const setAction = jest.fn
|
const setAction = jest.fn
|
||||||
const newBoardInfoIcon = (<i className="icon-information-outline" />)
|
const newBoardInfoIcon = (<i className='icon-information-outline'/>)
|
||||||
|
|
||||||
const {container} = render(wrapIntl(
|
const {container} = render(wrapIntl(
|
||||||
<ReduxProvider store={store}>
|
<ReduxProvider store={store}>
|
||||||
@@ -41,11 +40,11 @@ describe('components/createBoardFromTemplate', () => {
|
|||||||
expect(container).toMatchSnapshot()
|
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 store = mockStateStore([], state)
|
||||||
const setCanCreate = jest.fn
|
const setCanCreate = jest.fn
|
||||||
const setAction = jest.fn
|
const setAction = jest.fn
|
||||||
const newBoardInfoIcon = (<i className="icon-information-outline" />)
|
const newBoardInfoIcon = (<i className='icon-information-outline'/>)
|
||||||
|
|
||||||
render(wrapIntl(
|
render(wrapIntl(
|
||||||
<ReduxProvider store={store}>
|
<ReduxProvider store={store}>
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ import React, {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
useRef,
|
||||||
useState
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
|
|
||||||
import {createIntl, createIntlCache, IntlProvider} from 'react-intl'
|
import {IntlProvider, createIntl, createIntlCache} from 'react-intl'
|
||||||
|
|
||||||
import Select from 'react-select/async'
|
import Select from 'react-select/async'
|
||||||
import {
|
import {
|
||||||
components,
|
|
||||||
FormatOptionLabelMeta,
|
FormatOptionLabelMeta,
|
||||||
GroupBase,
|
GroupBase,
|
||||||
PlaceholderProps
|
PlaceholderProps,
|
||||||
|
SingleValue,
|
||||||
|
components,
|
||||||
} from 'react-select'
|
} from 'react-select'
|
||||||
import {SingleValue} from 'react-select'
|
|
||||||
|
|
||||||
import {CSSObject} from '@emotion/serialize'
|
import {CSSObject} from '@emotion/serialize'
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ const TEMPLATE_DESCRIPTION_LENGTH = 70
|
|||||||
const cache = createIntlCache()
|
const cache = createIntlCache()
|
||||||
const intl = createIntl({
|
const intl = createIntl({
|
||||||
locale: getCurrentLanguage(),
|
locale: getCurrentLanguage(),
|
||||||
messages: getMessages(getCurrentLanguage())
|
messages: getMessages(getCurrentLanguage()),
|
||||||
}, cache)
|
}, cache)
|
||||||
|
|
||||||
const {ValueContainer, Placeholder} = components
|
const {ValueContainer, Placeholder} = components
|
||||||
@@ -60,7 +60,7 @@ const CustomValueContainer = ({children, ...props}: any) => {
|
|||||||
{props.selectProps.placeholder}
|
{props.selectProps.placeholder}
|
||||||
</Placeholder>
|
</Placeholder>
|
||||||
{React.Children.map(children, (child) =>
|
{React.Children.map(children, (child) =>
|
||||||
child && child.type !== Placeholder ? child : null
|
(child && child.type !== Placeholder ? child : null)
|
||||||
)}
|
)}
|
||||||
</ValueContainer>
|
</ValueContainer>
|
||||||
)
|
)
|
||||||
@@ -78,7 +78,6 @@ const CreateBoardFromTemplate = (props: Props) => {
|
|||||||
const templateIdRef = useRef('')
|
const templateIdRef = useRef('')
|
||||||
templateIdRef.current = selectedBoardTemplateId
|
templateIdRef.current = selectedBoardTemplateId
|
||||||
|
|
||||||
|
|
||||||
const showNewBoardTemplateSelector = async () => {
|
const showNewBoardTemplateSelector = async () => {
|
||||||
setAddBoard((prev: boolean) => !prev)
|
setAddBoard((prev: boolean) => !prev)
|
||||||
}
|
}
|
||||||
@@ -86,14 +85,14 @@ const CreateBoardFromTemplate = (props: Props) => {
|
|||||||
// CreateBoardFromTemplate
|
// CreateBoardFromTemplate
|
||||||
const addBoardToChannel = async (channelId: string, teamId: string) => {
|
const addBoardToChannel = async (channelId: string, teamId: string) => {
|
||||||
if (!addBoardRef.current || !templateIdRef.current) {
|
if (!addBoardRef.current || !templateIdRef.current) {
|
||||||
return
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const ACTION_DESCRIPTION = 'board created from channel'
|
const ACTION_DESCRIPTION = 'board created from channel'
|
||||||
const LINKED_CHANNEL = 'linked channel'
|
const LINKED_CHANNEL = 'linked channel'
|
||||||
const asTemplate = false
|
const asTemplate = false
|
||||||
|
|
||||||
let boardsAndBlocks = undefined
|
let boardsAndBlocks
|
||||||
|
|
||||||
if (templateIdRef.current === EMPTY_BOARD) {
|
if (templateIdRef.current === EMPTY_BOARD) {
|
||||||
boardsAndBlocks = await mutator.addEmptyBoard(teamId, intl)
|
boardsAndBlocks = await mutator.addEmptyBoard(teamId, intl)
|
||||||
@@ -102,7 +101,8 @@ const CreateBoardFromTemplate = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const board = boardsAndBlocks.boards[0]
|
const board = boardsAndBlocks.boards[0]
|
||||||
await mutator.updateBoard({...board, channelId: channelId}, board, LINKED_CHANNEL)
|
await mutator.updateBoard({...board, channelId}, board, LINKED_CHANNEL)
|
||||||
|
|
||||||
return board
|
return board
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +127,7 @@ const CreateBoardFromTemplate = (props: Props) => {
|
|||||||
if (wordBreakingIndex === -1) {
|
if (wordBreakingIndex === -1) {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${str.substring(0, (len + wordBreakingIndex))}…`
|
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
|
// 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
|
const selectedOption = id === optionLabel.selectValue[0]?.id
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={id}>
|
<div key={id}>
|
||||||
<span className={`${cssPrefix}__icon`}>
|
<span className={`${cssPrefix}__icon`}>
|
||||||
@@ -176,8 +178,9 @@ const CreateBoardFromTemplate = (props: Props) => {
|
|||||||
templates.push(emptyBoard)
|
templates.push(emptyBoard)
|
||||||
|
|
||||||
if (value !== '') {
|
if (value !== '') {
|
||||||
templates = templates.filter(template => template.title.toLowerCase().includes(value.toLowerCase()))
|
templates = templates.filter((template) => template.title.toLowerCase().includes(value.toLowerCase()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return templates
|
return templates
|
||||||
}, [allTemplates])
|
}, [allTemplates])
|
||||||
|
|
||||||
@@ -202,10 +205,11 @@ const CreateBoardFromTemplate = (props: Props) => {
|
|||||||
}),
|
}),
|
||||||
valueContainer: (baseStyles: CSSObject): CSSObject => ({
|
valueContainer: (baseStyles: CSSObject): CSSObject => ({
|
||||||
...baseStyles,
|
...baseStyles,
|
||||||
overflow: 'visible'
|
overflow: 'visible',
|
||||||
}),
|
}),
|
||||||
placeholder: (baseStyles: CSSObject, state: PlaceholderProps<ReactSelectItem, false, GroupBase<ReactSelectItem>>): CSSObject => {
|
placeholder: (baseStyles: CSSObject, state: PlaceholderProps<ReactSelectItem, false, GroupBase<ReactSelectItem>>): CSSObject => {
|
||||||
const modifyPlaceholder = state.selectProps.menuIsOpen || (!state.selectProps.menuIsOpen && state.hasValue)
|
const modifyPlaceholder = state.selectProps.menuIsOpen || (!state.selectProps.menuIsOpen && state.hasValue)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...baseStyles,
|
...baseStyles,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@@ -257,12 +261,13 @@ const CreateBoardFromTemplate = (props: Props) => {
|
|||||||
|
|
||||||
const IntlCreateBoardFromTemplate = (props: Props) => {
|
const IntlCreateBoardFromTemplate = (props: Props) => {
|
||||||
const language = useAppSelector<string>(getLanguage)
|
const language = useAppSelector<string>(getLanguage)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider
|
<IntlProvider
|
||||||
locale={language.split(/[_]/)[0]}
|
locale={language.split(/[_]/)[0]}
|
||||||
messages={getMessages(language)}
|
messages={getMessages(language)}
|
||||||
>
|
>
|
||||||
<CreateBoardFromTemplate {...props}/>
|
<CreateBoardFromTemplate {...props}/>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useState, KeyboardEvent} from 'react'
|
import React, {KeyboardEvent, useState} from 'react'
|
||||||
|
|
||||||
import {useIntl} from 'react-intl'
|
import {useIntl} from 'react-intl'
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ const CreateCategory = (props: Props): JSX.Element => {
|
|||||||
const onCreate = async (categoryName: string) => {
|
const onCreate = async (categoryName: string) => {
|
||||||
if (!me) {
|
if (!me) {
|
||||||
Utils.logError('me not initialized')
|
Utils.logError('me not initialized')
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,10 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
|||||||
render(): React.ReactNode {
|
render(): React.ReactNode {
|
||||||
if (this.state.hasError) {
|
if (this.state.hasError) {
|
||||||
this.handleError()
|
this.handleError()
|
||||||
|
|
||||||
return <span>{this.msg}</span>
|
return <span>{this.msg}</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.props.children
|
return this.props.children
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,12 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
render,
|
|
||||||
act,
|
act,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
screen,
|
screen,
|
||||||
fireEvent
|
|
||||||
} from '@testing-library/react'
|
} from '@testing-library/react'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import {wrapIntl} from 'src/testUtils'
|
import {wrapIntl} from 'src/testUtils'
|
||||||
|
|
||||||
import FlashMessages, {sendFlashMessage} from './flashMessages'
|
import FlashMessages, {sendFlashMessage} from './flashMessages'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useState, useEffect} from 'react'
|
import React, {useEffect, useState} from 'react'
|
||||||
import {createNanoEvents} from 'nanoevents'
|
import {createNanoEvents} from 'nanoevents'
|
||||||
|
|
||||||
import './flashMessages.scss'
|
import './flashMessages.scss'
|
||||||
@@ -37,6 +37,7 @@ const FlashMessages = (props: Props) => {
|
|||||||
setMessage(newMessage)
|
setMessage(newMessage)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isSubscribed = false
|
isSubscribed = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react'
|
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'
|
import {Provider as ReduxProvider} from 'react-redux'
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ import userEvent from '@testing-library/user-event'
|
|||||||
|
|
||||||
import {mocked} from 'jest-mock'
|
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'
|
import {TestBlockFactory} from 'src/test/testBlockFactory'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useMemo, useCallback} from 'react'
|
import React, {useCallback, useMemo} from 'react'
|
||||||
import {FormattedMessage} from 'react-intl'
|
import {FormattedMessage} from 'react-intl'
|
||||||
|
|
||||||
import {Constants, Permission} from 'src/constants'
|
import {Constants, Permission} from 'src/constants'
|
||||||
|
|||||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user