diff --git a/app/client/.eslintrc.base.json b/app/client/.eslintrc.base.json index 0cc41a13652..14f56a13753 100644 --- a/app/client/.eslintrc.base.json +++ b/app/client/.eslintrc.base.json @@ -41,6 +41,28 @@ "project": "./tsconfig.json" }, "rules": { + "padding-line-between-statements": [ + "error", + { "blankLine": "always", "prev": "*", "next": "return" }, + { + "blankLine": "always", + "prev": "*", + "next": ["if", "for", "while"] + }, + { + "blankLine": "always", + "prev": ["if", "for", "while"], + "next": "*" + }, + { "blankLine": "always", "prev": ["const", "let"], "next": "*" }, + { + "blankLine": "any", + "prev": ["const", "let"], + "next": ["const", "let"] + }, + { "blankLine": "always", "prev": "*", "next": "function" }, + { "blankLine": "always", "prev": "function", "next": "*" } + ], "testing-library/consistent-data-testid": [ "warn", { @@ -72,7 +94,7 @@ "react-perf/jsx-no-new-array-as-prop": "warn", "react-perf/jsx-no-new-function-as-prop": "warn", "react-perf/jsx-no-jsx-as-prop": "warn", - "react-perf/jsx-no-new-object-as-prop": [ + "react-perf/jsx-no-new-object-as-prop": [ "warn", { // we are disabling this rule here for native component since it won't make much difference in performance diff --git a/app/client/cypress/.eslintrc.json b/app/client/cypress/.eslintrc.json index 559800a3d88..6474182879c 100644 --- a/app/client/cypress/.eslintrc.json +++ b/app/client/cypress/.eslintrc.json @@ -18,6 +18,7 @@ "@typescript-eslint/adjacent-overload-signatures": "off", "jest/no-disabled-tests": "off", "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-var-requires": "off" + "@typescript-eslint/no-var-requires": "off", + "padding-line-between-statements": "off" } } diff --git a/app/client/packages/ast/src/actionCreator/index.test.ts b/app/client/packages/ast/src/actionCreator/index.test.ts index 32e5aedd052..e059aad0774 100644 --- a/app/client/packages/ast/src/actionCreator/index.test.ts +++ b/app/client/packages/ast/src/actionCreator/index.test.ts @@ -153,6 +153,7 @@ describe("getActionBlocks", () => { "Api1.run(() => setAlert('Success'), () => {});showModal('Modal1')", 2, ); + expect(result).toEqual([ "Api1.run(() => setAlert('Success'), () => {});", "showModal('Modal1');", @@ -162,6 +163,7 @@ describe("getActionBlocks", () => { it("should return an array of action blocks", () => { const value = "Api1.run(() => {\n (() => {});\n}, () => {}, {});"; const result = getActionBlocks(value, 2); + expect(result).toEqual([ "Api1.run(() => {\n (() => {});\n}, () => {}, {});", ]); @@ -285,6 +287,7 @@ describe("getThenCatchBlocksFromQuery", () => { it("should return then/catch callbacks appropriately", () => { const value = "Api1.run().catch(() => { a() }).then(() => { b() });"; const result = getThenCatchBlocksFromQuery(value, 2); + expect(result).toEqual({ then: `() => {\n b();\n}`, catch: `() => {\n a();\n}`, @@ -362,13 +365,16 @@ describe("Tests AST methods around function arguments", function () { it("Sets argument at 0th index", function () { const code1 = 'showAlert("", "")'; const modified1 = setTextArgumentAtPosition(code1, "Hello", 0, 2); + expect(modified1).toEqual(`{{showAlert("Hello", "");}}`); const code2 = 'showAlert("", 2).then(() => "Hello")'; const modified2 = setTextArgumentAtPosition(code2, "Hello", 0, 2); + expect(modified2).toEqual(`{{showAlert("Hello", 2).then(() => "Hello");}}`); const arg1 = getEnumArgumentAtPosition(code2, 1, "", 2); + expect(arg1).toBe("2"); }); }); @@ -407,10 +413,12 @@ describe("Test canTranslateToUI methoda", () => { expected: false, }, ]; + test.each(cases.map((x) => [x.index, x.input, x.expected]))( "test case %d", (_, input, expected) => { const result = canTranslateToUI(input as string, 2); + expect(result).toEqual(expected); }, ); diff --git a/app/client/packages/ast/src/actionCreator/index.ts b/app/client/packages/ast/src/actionCreator/index.ts index 32b9c0c6c9d..fec4978b44f 100644 --- a/app/client/packages/ast/src/actionCreator/index.ts +++ b/app/client/packages/ast/src/actionCreator/index.ts @@ -39,10 +39,12 @@ export const getTextArgumentAtPosition = ( let requiredArgument: any = ""; const commentArray: Array = []; let astWithComments; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(value, evaluationVersion); const wrappedCode = wrapCode(sanitizedScript); + ast = getAST(wrappedCode, { locations: true, ranges: true, @@ -60,6 +62,7 @@ export const getTextArgumentAtPosition = ( if (node && isCallExpressionNode(node)) { const argument = node.arguments[argNum]; + // return appropriate values based on the type of node switch (argument?.type) { case NodeTypes.Identifier: @@ -114,9 +117,11 @@ export const setTextArgumentAtPosition = ( typeof changeValue === "string" ? String.raw`"${changeValue}"` : String.raw`${changeValue}`; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const changeValueScript = sanitizeScript(rawValue, evaluationVersion); + getAST(changeValueScript, { locations: true, ranges: true, @@ -128,6 +133,7 @@ export const setTextArgumentAtPosition = ( // collect all comments as they are not part of the ast, we will attach them back on line 46 onComment: commentArray, }); + // clone ast to avoid mutating original ast ast = klona(__ast); // attach comments to ast @@ -141,6 +147,7 @@ export const setTextArgumentAtPosition = ( if (node && isCallExpressionNode(node)) { const startPosition = node.callee.end + NEXT_POSITION; + node.arguments = node.arguments || []; node.arguments[argNum] = { type: NodeTypes.Literal, @@ -179,9 +186,11 @@ export const setCallbackFunctionField = ( | CallExpressionNode | BlockStatementNode | LiteralNode; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(currentValue, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -190,6 +199,7 @@ export const setCallbackFunctionField = ( }); const sanitizedChangeValue = sanitizeScript(changeValue, evaluationVersion); + changeValueAst = getAST(sanitizedChangeValue, { locations: true, ranges: true, @@ -231,6 +241,7 @@ export const setCallbackFunctionField = ( if (found) { const { node } = found; + // When there is an argument after the specified argument number, then only add empty string literal // @ts-expect-error: types not matched if (changeValue === "" && node.arguments[argNum + 1]) { @@ -281,11 +292,13 @@ export const setObjectAtPosition = ( ) { changeValue = "{}"; } + changeValue = changeValue.trim(); let ast: Node = { end: 0, start: 0, type: "" }; let changedValue: string = currentValue; const commentArray: Array = []; let astWithComments; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(currentValue, evaluationVersion); @@ -295,6 +308,7 @@ export const setObjectAtPosition = ( // collect all comments as they are not part of the ast, we will attach them back on line 46 onComment: commentArray, }); + // clone ast to avoid mutating original ast ast = klona(__ast); @@ -306,8 +320,10 @@ export const setObjectAtPosition = ( } const node = findRootCallExpression(astWithComments); + if (node && isCallExpressionNode(node)) { const startPosition = node.callee.end + NEXT_POSITION; + node.arguments[argNum] = { type: NodeTypes.Literal, value: changeValue, @@ -339,9 +355,11 @@ export const getEnumArgumentAtPosition = ( let requiredArgument: string = defaultValue; const commentArray: Array = []; let astWithComments; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(value, evaluationVersion); + // const wrappedCode = wrapCode(sanitizedScript); ast = getAST(sanitizedScript, { locations: true, @@ -364,6 +382,7 @@ export const getEnumArgumentAtPosition = ( if (node && isCallExpressionNode(node)) { if (node.arguments[argNum]) { const argument = node.arguments[argNum]; + switch (argument?.type) { case NodeTypes.Literal: requiredArgument = argument.raw as string; @@ -387,6 +406,7 @@ export const setEnumArgumentAtPosition = ( let changedValue: string = currentValue; const commentArray: Array = []; let astWithComments; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(currentValue, evaluationVersion); @@ -396,6 +416,7 @@ export const setEnumArgumentAtPosition = ( // collect all comments as they are not part of the ast, we will attach them back on line 46 onComment: commentArray, }); + // clone ast to avoid mutating original ast ast = klona(__ast); @@ -417,6 +438,7 @@ export const setEnumArgumentAtPosition = ( // add 1 to get the starting position of the next // node to ending position of previous const startPosition = node.callee.end + NEXT_POSITION; + node.arguments[argNum] = { type: NodeTypes.Literal, value: `${changeValue}`, @@ -442,10 +464,12 @@ export const getModalName = ( let modalName = "none"; const commentArray: Array = []; let astWithComments; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(value, evaluationVersion); const wrappedCode = wrapCode(sanitizedScript); + ast = getAST(wrappedCode, { locations: true, ranges: true, @@ -463,6 +487,7 @@ export const getModalName = ( if (node && isCallExpressionNode(node)) { const argument = node.arguments[0]; + switch (argument?.type) { case NodeTypes.Literal: modalName = argument.value as string; @@ -490,6 +515,7 @@ export const setModalName = ( let changedValue: string = currentValue; const commentArray: Array = []; let astWithComments; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(currentValue, evaluationVersion); @@ -499,6 +525,7 @@ export const setModalName = ( // collect all comments as they are not part of the ast, we will attach them back on line 46 onComment: commentArray, }); + // clone ast to avoid mutating original ast ast = klona(__ast); @@ -510,6 +537,7 @@ export const setModalName = ( } const node = findRootCallExpression(astWithComments); + if (node && isCallExpressionNode(node)) { // add 1 to get the starting position of the next // node to ending position of previous @@ -522,11 +550,13 @@ export const setModalName = ( // add 2 for quotes end: startPosition + (changeValue.length + LENGTH_OF_QUOTES), }; + node.arguments = [newNode]; changedValue = `{{${generate(astWithComments, { comments: true, }).trim()}}}`; } + return changedValue; }; @@ -539,9 +569,11 @@ export const getFuncExpressionAtPosition = ( let ast: Node = { end: 0, start: 0, type: "" }; let requiredArgument = "() => {}"; const commentArray: Array = []; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -562,6 +594,7 @@ export const getFuncExpressionAtPosition = ( const firstCallExpressionNode = findRootCallExpression(astWithComments); const argumentNode = firstCallExpressionNode?.arguments[argNum]; + if ( argumentNode && (isTypeOfFunction(argumentNode.type) || @@ -588,10 +621,12 @@ export const getFunction = ( let requiredFunction = ""; const commentArray: Array = []; let astWithComments; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(value, evaluationVersion); const wrappedCode = wrapCode(sanitizedScript); + ast = getAST(wrappedCode, { locations: true, ranges: true, @@ -609,6 +644,7 @@ export const getFunction = ( if (node && isCallExpressionNode(node)) { const func = `${generate(node)}`; + requiredFunction = func !== "{}" ? `{{${func}}}` : ""; } @@ -637,9 +673,11 @@ export const replaceActionInQuery = ( const commentArray: Array = []; const changeActionCommentArray: Array = []; let astWithComments: any, changeActionAstWithComments; + try { // sanitize to remove unnecessary characters which might lead to invalid ast const sanitizedScript = sanitizeScript(query, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -651,6 +689,7 @@ export const replaceActionInQuery = ( changeAction, evaluationVersion, ); + changeActionAst = getAST(sanitizedChangeAction, { locations: true, ranges: true, @@ -687,6 +726,7 @@ export const replaceActionInQuery = ( // add 1 to get the starting position of the next // node to ending position of previous const startPosition = node.arguments[argNum].start; + requiredNode.start = startPosition; requiredNode.end = startPosition + changeAction.length; node.arguments[argNum] = requiredNode; @@ -711,8 +751,10 @@ export function getActionBlocks( const commentArray: Array = []; const actionBlocks: Array = []; let astWithComments; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -741,8 +783,10 @@ export function canTranslateToUI( const commentArray: Array = []; let canTranslate = true; let astWithComments; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -777,13 +821,16 @@ export function canTranslateToUI( for (const node of astWithComments.body) { if (isExpressionStatementNode(node)) { const expression = node.expression; + if (!isCallExpressionNode(expression)) { canTranslate = false; break; } + const rootCallExpression = findRootCallExpression( expression, ) as CallExpressionNode; + if (!rootCallExpression) { canTranslate = false; break; @@ -792,6 +839,7 @@ export function canTranslateToUI( canTranslate = false; } } + return canTranslate; } @@ -801,8 +849,10 @@ export function getFunctionBodyStatements( ): Array { let ast: Node = { end: 0, start: 0, type: "" }; const commentArray: Array = []; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -821,11 +871,13 @@ export function getFunctionBodyStatements( statementsBody = mainBody.expression.body.body; else if (mainBody.expression.body.type === NodeTypes.CallExpression) statementsBody = [mainBody.expression.body]; + break; case NodeTypes.FunctionDeclaration: statementsBody = mainBody.body.body; break; } + return statementsBody.map((node: Node) => generate(node, { comments: true }).trim(), ); @@ -842,8 +894,10 @@ export function getMainAction( const commentArray: Array = []; let mainAction = ""; let astWithComments; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -881,8 +935,10 @@ export function getFunctionName( let ast: Node = { end: 0, start: 0, type: "" }; const commentArray: Array = []; const functionName = ""; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -911,8 +967,10 @@ export function getThenCatchBlocksFromQuery( let ast: Node = { end: 0, start: 0, type: "" }; const commentArray: Array = []; const returnValue: Record = {}; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -922,6 +980,7 @@ export function getThenCatchBlocksFromQuery( const astWithComments = attachCommentsToAst(ast, commentArray); const rootCallExpression = findRootCallExpression(astWithComments); + if (!rootCallExpression) return returnValue; let firstBlockType; @@ -937,24 +996,29 @@ export function getThenCatchBlocksFromQuery( if (isIdentifierNode(node.callee.property)) { if (["then", "catch"].includes(node.callee.property.name)) { firstBlockType = node.callee.property.name; + return true; } } } } } + return false; }, )?.node; if (!firstBlock) return returnValue; + if (!isCallExpressionNode(firstBlock) || !firstBlockType) return returnValue; const args = firstBlock.arguments; + if (args.length) { returnValue[firstBlockType] = generate(args[0]); } + const secondBlockType = firstBlockType === "then" ? "catch" : "then"; const secondBlock = findNodeAt(ast, 0, undefined, function (type, node) { if (isCallExpressionNode(node)) { @@ -965,11 +1029,13 @@ export function getThenCatchBlocksFromQuery( } } } + return false; })?.node; if (secondBlock && isCallExpressionNode(secondBlock)) { const args = secondBlock.arguments; + if (args.length > 0) { returnValue[secondBlockType] = generate(args[0]); } @@ -989,6 +1055,7 @@ export function setThenBlockInQuery( let ast: Node = { end: 0, start: 0, type: "" }; const commentArray: Array = []; let requiredQuery = ""; + thenBlock = thenBlock || "() => {}"; try { const sanitizedScript = sanitizeScript(value, evaluationVersion); @@ -1016,6 +1083,7 @@ export function setThenBlockInQuery( } } } + return false; }, )?.node; @@ -1040,6 +1108,7 @@ export function setThenBlockInQuery( }, }, }; + astWithComments.body[0].expression = callExpression; astWithComments.body[0].end = callExpression.end; } @@ -1058,6 +1127,7 @@ export function setThenBlockInQuery( } } } + return false; }, )?.node; @@ -1095,9 +1165,11 @@ export function setCatchBlockInQuery( let ast: Node = { end: 0, start: 0, type: "" }; const commentArray: Array = []; let requiredQuery = ""; + catchBlock = catchBlock || "() => {}"; try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -1112,12 +1184,14 @@ export function setCatchBlockInQuery( rootCallExpression, "catch", ); + if (!catchCallExpressionInGivenQuery) { const thenCallExpressionInGivenQuery = findNodeWithCalleeAndProperty( astWithComments, rootCallExpression, "then", ); + catchCallExpressionInGivenQuery = thenCallExpressionInGivenQuery && findNodeWithCalleeAndProperty( @@ -1125,6 +1199,7 @@ export function setCatchBlockInQuery( thenCallExpressionInGivenQuery, "catch", ); + if (!catchCallExpressionInGivenQuery) { const expression = klona( thenCallExpressionInGivenQuery ?? rootCallExpression, @@ -1146,6 +1221,7 @@ export function setCatchBlockInQuery( }, }, }; + catchCallExpressionInGivenQuery = callExpression; astWithComments.body[0].expression = catchCallExpressionInGivenQuery; } @@ -1184,8 +1260,10 @@ export function getFunctionArguments( const commentArray: Array = []; const argumentsArray: Array = []; let astWithComments; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -1215,8 +1293,10 @@ export function getFunctionNameFromJsObjectExpression( const commentArray: Array = []; let functionName = ""; let astWithComments; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -1249,8 +1329,10 @@ export function getCallExpressions( const commentArray: Array = []; const callExpressions: Array = []; let astWithComments; + try { const sanitizedScript = sanitizeScript(value, evaluationVersion); + ast = getAST(sanitizedScript, { locations: true, ranges: true, @@ -1291,6 +1373,7 @@ function findRootCallExpression(ast: Node) { * pick the one the that has the least end offset. */ let rootCallExpression = callExpressions[0]; + for (const ce of callExpressions) { if (rootCallExpression.start === ce.start) { rootCallExpression = @@ -1309,6 +1392,7 @@ function findNodeWithCalleeAndProperty( property?: string, ) { if (!ast || !callee || !property) return undefined; + return findNodeAt(ast, 0, undefined, function (type, node) { if (isCallExpressionNode(node)) { if (isMemberExpressionNode(node.callee)) { @@ -1319,6 +1403,7 @@ function findNodeWithCalleeAndProperty( } } } + return false; })?.node; } @@ -1326,6 +1411,7 @@ function findNodeWithCalleeAndProperty( export function getFunctionParams(code: string, evaluationVersion: number) { try { const sanitizedScript = sanitizeScript(code, evaluationVersion); + code = `let a = ${sanitizedScript.trim()}`; const ast = getAST(code, { locations: true, @@ -1336,6 +1422,7 @@ export function getFunctionParams(code: string, evaluationVersion: number) { const params = functionExpression.params?.map((param: any) => generate(param).trim()) || []; + return params; } catch (e) { return []; @@ -1355,20 +1442,25 @@ export function getQueryParam( }); const rootCallExpression = findRootCallExpression(ast); + if (!rootCallExpression) return `{{ {} }}`; const args = rootCallExpression.arguments; + if (!args || args.length === 0) return `{{{}}}`; const firstArg = args[0] || ({} as ArgumentTypes); + if (firstArg.type && !isTypeOfFunction(firstArg.type)) { return getTextArgumentAtPosition(code, 0, evaluationVersion); } const thirdArg = args[2] || ({} as ArgumentTypes); + if (thirdArg.type && !isTypeOfFunction(thirdArg.type)) { return getTextArgumentAtPosition(code, 2, evaluationVersion); } + return `{{{}}}`; } catch (e) { return `{{{}}}`; @@ -1389,22 +1481,27 @@ export function setQueryParam( }); const rootCallExpression = findRootCallExpression(ast); + if (!rootCallExpression) return code; if (position === 0) { rootCallExpression.arguments = []; code = generate(ast); + return setObjectAtPosition(code, value, position, evaluationVersion); } else { const firstArg = rootCallExpression.arguments[0] || ({} as ArgumentTypes); const secondArg = rootCallExpression.arguments[1] || ({} as ArgumentTypes); + if (firstArg && !isTypeOfFunction(firstArg.type)) { code = setCallbackFunctionField(code, "() => {}", 0, evaluationVersion); } + if (secondArg && !isTypeOfFunction(secondArg.type)) { code = setCallbackFunctionField(code, "() => {}", 1, evaluationVersion); } + return setObjectAtPosition(code, value, 2, evaluationVersion); } } catch (e) { @@ -1423,20 +1520,27 @@ export function checkIfThenBlockExists( ranges: true, }); const rootCallExpression = findRootCallExpression(ast); + if (!rootCallExpression) return code; + let thenBlock = findNodeWithCalleeAndProperty( ast, rootCallExpression, "then", ); + if (thenBlock) return true; + const catchBlock = findNodeWithCalleeAndProperty( ast, rootCallExpression, "catch", ); + thenBlock = findNodeWithCalleeAndProperty(ast, catchBlock, "then"); + if (thenBlock) return true; + return false; } catch (e) { return false; @@ -1454,20 +1558,27 @@ export function checkIfCatchBlockExists( ranges: true, }); const rootCallExpression = findRootCallExpression(ast); + if (!rootCallExpression) return code; + let catchBlock = findNodeWithCalleeAndProperty( ast, rootCallExpression, "catch", ); + if (catchBlock) return true; + const thenBlock = findNodeWithCalleeAndProperty( ast, rootCallExpression, "then", ); + catchBlock = findNodeWithCalleeAndProperty(ast, thenBlock, "catch"); + if (catchBlock) return true; + return false; } catch (e) { return false; @@ -1486,9 +1597,13 @@ export function checkIfArgumentExistAtPosition( ranges: true, }); const rootCallExpression = findRootCallExpression(ast); + if (!rootCallExpression) return false; + const args = rootCallExpression.arguments; + if (!args || args.length === 0 || !args[position]) return false; + return true; } catch (e) { return false; @@ -1509,12 +1624,14 @@ export function setGenericArgAtPostition( ranges: true, onComment: commentArray, }); + arg = arg.trim(); const astWithComments = attachCommentsToAst(ast, commentArray); let argAst; let argASTWithComments; let argNode; + try { argAst = getAST(arg, { locations: true, @@ -1522,9 +1639,11 @@ export function setGenericArgAtPostition( onComment: argCommentArray, }); argASTWithComments = attachCommentsToAst(argAst, argCommentArray); + if (isBlockStatementNode(argASTWithComments.body[0])) { throw "Object interpretted as Block statement"; } + argNode = argASTWithComments.body[0].expression; } catch (e) { // If the arg is { a: 2 }, ast will BlockStatement and would end up here. @@ -1539,10 +1658,14 @@ export function setGenericArgAtPostition( } const rootCallExpression = findRootCallExpression(astWithComments); + if (!rootCallExpression) return code; + const args = rootCallExpression.arguments || []; + args[position] = argNode; rootCallExpression.arguments = args; + return generate(ast).trim(); } catch (e) { return code; diff --git a/app/client/packages/ast/src/index.test.ts b/app/client/packages/ast/src/index.test.ts index fbc64410d59..1c4e397aba2 100644 --- a/app/client/packages/ast/src/index.test.ts +++ b/app/client/packages/ast/src/index.test.ts @@ -312,6 +312,7 @@ describe("getAllIdentifiers", () => { 2, perCase.invalidIdentifiers, ); + expect(references).toStrictEqual(perCase.expectedResults); }); }); @@ -402,6 +403,7 @@ describe("parseJSObjectWithAST", () => { }, ]; const { parsedObject } = parseJSObject(body); + expect(parsedObject).toStrictEqual(expectedParsedObject); }); @@ -490,6 +492,7 @@ describe("parseJSObjectWithAST", () => { }, ]; const { parsedObject } = parseJSObject(body); + expect(parsedObject).toStrictEqual(expectedParsedObject); }); @@ -563,6 +566,7 @@ describe("parseJSObjectWithAST", () => { }, ]; const { parsedObject } = parseJSObject(body); + expect(parsedObject).toStrictEqual(expectedParsedObject); }); @@ -622,6 +626,7 @@ describe("parseJSObjectWithAST", () => { }, ]; const { parsedObject } = parseJSObject(body); + expect(parsedObject).toStrictEqual(expectedParsedObject); }); }); @@ -719,6 +724,7 @@ describe("getMemberExpressionObjectFromProperty", () => { propertyName, code, ); + expect(actualResponse).toStrictEqual([]); }); it("returns an empty array for invalid js", () => { @@ -731,6 +737,7 @@ describe("getMemberExpressionObjectFromProperty", () => { propertyName, code, ); + expect(actualResponse).toStrictEqual([]); }); it("returns correct member expression object(s)", () => { @@ -783,6 +790,7 @@ describe("getMemberExpressionObjectFromProperty", () => { testDatum.propertyName, testDatum.code, ); + expect(actualResponse).toStrictEqual(testDatum.expectedResponse); } }); diff --git a/app/client/packages/ast/src/index.ts b/app/client/packages/ast/src/index.ts index 3f9e1daf0eb..fcf2a195ad6 100644 --- a/app/client/packages/ast/src/index.ts +++ b/app/client/packages/ast/src/index.ts @@ -267,6 +267,7 @@ const isFunctionDeclaration = (node: Node): node is FunctionDeclarationNode => { const isFunctionExpression = (node: Node): node is FunctionExpressionNode => { return node.type === NodeTypes.FunctionExpression; }; + export const isArrowFunctionExpression = ( node: Node, ): node is ArrowFunctionExpressionNode => { @@ -361,6 +362,7 @@ export const wrapCode = (code: string) => { //Used slice for a quick resolve of critical bug const unwrapCode = (code: string) => { const unwrapedCode = code.slice(32); + return unwrapedCode.slice(0, -10); }; @@ -407,6 +409,7 @@ export const extractIdentifierInfoFromCode = ( invalidIdentifiers?: Record, ): IdentifierInfo => { let ast: Node = { end: 0, start: 0, type: "" }; + try { const sanitizedScript = sanitizeScript(code, evaluationVersion); /* wrapCode - Wrapping code in a function, since all code/script get wrapped with a function during evaluation. @@ -419,6 +422,7 @@ export const extractIdentifierInfoFromCode = ( let result = function() { return 123; }() -> is valid */ const wrappedCode = wrapCode(sanitizedScript); + ast = getAST(wrappedCode); const { functionalParams, references, variableDeclarations }: NodeList = ancestorWalk(ast); @@ -426,12 +430,14 @@ export const extractIdentifierInfoFromCode = ( // To remove references derived from declared variables and function params, // We extract the topLevelIdentifier Eg. Api1.name => Api1 const topLevelIdentifier = toPath(reference)[0]; + return !( functionalParams.has(topLevelIdentifier) || variableDeclarations.has(topLevelIdentifier) || has(invalidIdentifiers, topLevelIdentifier) ); }); + return { references: referencesArr, functionalParams: Array.from(functionalParams), @@ -448,6 +454,7 @@ export const extractIdentifierInfoFromCode = ( isError: true, }; } + throw e; } }; @@ -465,6 +472,7 @@ export const entityRefactorFromCode = ( //If script is a JSObject then replace export default to decalartion. if (isJSObject) script = jsObjectToCode(script); else script = wrapCode(script); + let ast: Node = { end: 0, start: 0, type: "" }; //Copy of script to refactor let refactorScript = script; @@ -474,6 +482,7 @@ export const entityRefactorFromCode = ( let refactorOffset = 0; //Count of refactors on the script let refactorCount = 0; + try { ast = getAST(script); const { @@ -491,16 +500,19 @@ export const entityRefactorFromCode = ( // To remove references derived from declared variables and function params, // We extract the topLevelIdentifier Eg. Api1.name => Api1 const topLevelIdentifier = toPath(reference)[0]; + return !( functionalParams.has(topLevelIdentifier) || variableDeclarations.has(topLevelIdentifier) || has(invalidIdentifiers, topLevelIdentifier) ); }); + //Traverse through all identifiers in the script identifierArray.forEach((identifier) => { if (identifier.name === oldNameArr[0]) { let index = 0; + while (index < referencesArr.length) { if (identifier.name === referencesArr[index].split(".")[0]) { //Replace the oldName by newName @@ -518,12 +530,14 @@ export const entityRefactorFromCode = ( oldNameArr.length > 1 && propertyNode && oldNameArr[1] === propertyNode.name; + //Condition to validate if Identifier || Property should be updated?? if (oldNameArr.length === 1 || propertyCondFlag) { //Condition to extend end index in case of property match if (propertyCondFlag && propertyNode) { endIndex = propertyNode.end; } + refactorScript = refactorScript.substring(0, identifier.start + refactorOffset) + newName + @@ -534,13 +548,16 @@ export const entityRefactorFromCode = ( break; } } + index++; } } }); + //If script is a JSObject then revert decalartion to export default. if (isJSObject) refactorScript = jsCodeToObject(refactorScript); else refactorScript = unwrapCode(refactorScript); + return { isSuccess: true, body: { script: refactorScript, refactorCount }, @@ -550,6 +567,7 @@ export const entityRefactorFromCode = ( // Syntax error. Ignore and return empty list return { isSuccess: false, body: { error: "Syntax Error" } }; } + throw e; } }; @@ -568,6 +586,7 @@ export const getFunctionalParamsFromNode = ( code = "", ): Set => { const functionalParams = new Set(); + node.params.forEach((paramNode) => { if (isIdentifierNode(paramNode)) { functionalParams.add({ @@ -577,6 +596,7 @@ export const getFunctionalParamsFromNode = ( } else if (isAssignmentPatternNode(paramNode)) { if (isIdentifierNode(paramNode.left)) { const paramName = paramNode.left.name; + if (!needValue || !code) { functionalParams.add({ paramName, defaultValue: undefined }); } else { @@ -589,6 +609,7 @@ export const getFunctionalParamsFromNode = ( typeof paramNode.right.value === "string" ? paramNode.right.value : `{{${defaultValueInString}}}`; + functionalParams.add({ paramName, defaultValue, @@ -624,6 +645,7 @@ export const getFunctionalParamsFromNode = ( } } }); + return functionalParams; }; @@ -632,11 +654,13 @@ const constructFinalMemberExpIdentifier = ( child = "", ): string => { const propertyAccessor = getPropertyAccessor(node.property); + if (isIdentifierNode(node.object)) { return `${node.object.name}${propertyAccessor}${child}`; } else { const propertyAccessor = getPropertyAccessor(node.property); const nestedChild = `${propertyAccessor}${child}`; + return constructFinalMemberExpIdentifier( node.object as MemberExpressionNode, nestedChild, @@ -729,9 +753,11 @@ export const extractExpressionsFromCode = ( const variableDeclarations = new Set(); let functionalParams = new Set(); let ast: Node = { end: 0, start: 0, type: "" }; + try { const sanitizedScript = sanitizeScript(code, evaluationVersion); const wrappedCode = wrapCode(sanitizedScript); + ast = getAST(wrappedCode, { locations: true }); } catch (e) { if (e instanceof SyntaxError) { @@ -743,6 +769,7 @@ export const extractExpressionsFromCode = ( memberCallExpressionData: [], }; } + throw e; } simple(ast, { @@ -752,7 +779,9 @@ export const extractExpressionsFromCode = ( // We are only interested in top-level MemberExpression nodes // Eg. for Api1.data.name, we are only interested in Api1.data if (!isIdentifierNode(object)) return; + if (!(object.name in data) || !isTrueObject(data[object.name])) return; + // For computed member expressions (assessed via [], eg. JSObject1["name"] ), // We are only interested in strings if ( @@ -765,6 +794,7 @@ export const extractExpressionsFromCode = ( property, } as MemberExpressionData); } + // We ignore computed member expressions if property is an identifier (JSObject[name]) // This is because we can't statically determine what the value of the identifier might be. if ( @@ -785,6 +815,7 @@ export const extractExpressionsFromCode = ( }, FunctionDeclaration(node: Node) { if (!isFunctionDeclaration(node)) return; + functionalParams = new Set([ ...functionalParams, ...getFunctionalParamNamesFromNode(node), @@ -792,6 +823,7 @@ export const extractExpressionsFromCode = ( }, FunctionExpression(node: Node) { if (!isFunctionExpression(node)) return; + functionalParams = new Set([ ...functionalParams, ...getFunctionalParamNamesFromNode(node), @@ -799,6 +831,7 @@ export const extractExpressionsFromCode = ( }, ArrowFunctionExpression(node: Node) { if (!isArrowFunctionExpression(node)) return; + functionalParams = new Set([ ...functionalParams, ...getFunctionalParamNamesFromNode(node), @@ -885,8 +918,10 @@ const ancestorWalk = (ast: Node): NodeList => { let candidateTopLevelNode: IdentifierNode | MemberExpressionNode = node as IdentifierNode; let depth = ancestors.length - 2; // start "depth" with first parent + while (depth > 0) { const parent = ancestors[depth]; + if ( isMemberExpressionNode(parent) && /* Member expressions that are "computed" (with [ ] search) @@ -907,15 +942,18 @@ const ancestorWalk = (ast: Node): NodeList => { break; } } + //If parent is a Member expression then attach property to the Node. //else push Identifier Node. const parentNode = ancestors[ancestors.length - 2]; + if (isMemberExpressionNode(parentNode)) { identifierList.push({ ...(node as IdentifierNode), property: parentNode.property as IdentifierNode, }); } else identifierList.push(node as RefactorIdentifierNode); + if (isIdentifierNode(candidateTopLevelNode)) { // If the node is an Identifier, just save that references.add(candidateTopLevelNode.name); @@ -925,6 +963,7 @@ const ancestorWalk = (ast: Node): NodeList => { const memberExpIdentifier = constructFinalMemberExpIdentifier( candidateTopLevelNode, ); + references.add(memberExpIdentifier); } }, @@ -939,6 +978,7 @@ const ancestorWalk = (ast: Node): NodeList => { // params in function declarations are also counted as references so we keep // track of them and remove them from the final list of references if (!isFunctionDeclaration(node)) return; + functionalParams = new Set([ ...functionalParams, ...getFunctionalParamNamesFromNode(node), @@ -948,6 +988,7 @@ const ancestorWalk = (ast: Node): NodeList => { // params in function expressions are also counted as references so we keep // track of them and remove them from the final list of references if (!isFunctionExpression(node)) return; + functionalParams = new Set([ ...functionalParams, ...getFunctionalParamNamesFromNode(node), @@ -957,12 +998,14 @@ const ancestorWalk = (ast: Node): NodeList => { // params in arrow function expressions are also counted as references so we keep // track of them and remove them from the final list of references if (!isArrowFunctionExpression(node)) return; + functionalParams = new Set([ ...functionalParams, ...getFunctionalParamNamesFromNode(node), ]); }, }); + return { references, functionalParams, @@ -995,6 +1038,7 @@ export const isFunctionPresent = ( }); let isFunction = false; + simple(ast, { FunctionDeclaration() { isFunction = true; @@ -1019,25 +1063,33 @@ export function getMemberExpressionObjectFromProperty( evaluationVersion = 2, ) { if (!propertyName) return []; + const memberExpressionObjects = new Set(); let ast: Node = { end: 0, start: 0, type: "" }; + try { const sanitizedScript = sanitizeScript(code, evaluationVersion); const wrappedCode = wrapCode(sanitizedScript); + ast = getAST(wrappedCode, { locations: true }); simple(ast, { MemberExpression(node: Node) { const { object, property } = node as MemberExpressionNode; + if (!isLiteralNode(property) && !isIdentifierNode(property)) return; + const propName = isLiteralNode(property) ? property.value : property.name; + if (!isNil(propName) && getStringValue(propName) === propertyName) { const memberExpressionObjectString = generate(object); + memberExpressionObjects.add(memberExpressionObjectString); } }, }); + return Array.from(memberExpressionObjects); } catch (e) { return []; diff --git a/app/client/packages/ast/src/jsObject/index.test.ts b/app/client/packages/ast/src/jsObject/index.test.ts index 5a688d0dc45..4b092639c5e 100644 --- a/app/client/packages/ast/src/jsObject/index.test.ts +++ b/app/client/packages/ast/src/jsObject/index.test.ts @@ -32,6 +32,7 @@ describe("addPropertiesToJSObjectCode", () => { const ast = parseAST(result); let properties; + simple(ast, { ExportDefaultDeclaration(node) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -73,6 +74,7 @@ describe("addPropertiesToJSObjectCode", () => { const ast = parseAST(result); let properties; + simple(ast, { ExportDefaultDeclaration(node) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -132,6 +134,7 @@ async function myFun2() { }; const result = addPropertiesToJSObjectCode(body, obj); + expect(result).toEqual(body); }); }); diff --git a/app/client/packages/ast/src/jsObject/index.ts b/app/client/packages/ast/src/jsObject/index.ts index 589df0bc670..96df9e01659 100644 --- a/app/client/packages/ast/src/jsObject/index.ts +++ b/app/client/packages/ast/src/jsObject/index.ts @@ -65,9 +65,11 @@ export const isJSFunctionProperty = ( export const parseJSObject = (code: string) => { let ast: Node = { end: 0, start: 0, type: "" }; const result: TParsedJSProperty[] = []; + try { const comments: any = []; const token: any = []; + ast = getAST(code, { sourceType: SourceType.module, onComment: comments, @@ -90,6 +92,7 @@ export const parseJSObject = (code: string) => { !isObjectExpression(node.declaration) ) return; + JSObjectProperties = node.declaration .properties as NodeWithLocation[]; }, @@ -127,6 +130,7 @@ export const parseJSObject = (code: string) => { if (isPropertyAFunctionNode(node.value)) { const params = getFunctionalParamsFromNode(node.value, true, code); + property = { ...property, arguments: [...params], @@ -182,6 +186,7 @@ export const addPropertiesToJSObjectCode = ( }); }, }); + return escodegen.generate(ast); } catch (e) { return code; diff --git a/app/client/packages/ast/src/peekOverlay/index.test.ts b/app/client/packages/ast/src/peekOverlay/index.test.ts index bcfb1bbcb92..4d376d09cd7 100644 --- a/app/client/packages/ast/src/peekOverlay/index.test.ts +++ b/app/client/packages/ast/src/peekOverlay/index.test.ts @@ -16,6 +16,7 @@ describe("extractExpressionAtPositionWholeDoc", () => { resultString?: string, ) => { let result; + try { result = await scriptIdentifier.extractExpressionAtPosition(pos); } catch (e) { @@ -31,6 +32,7 @@ describe("extractExpressionAtPositionWholeDoc", () => { resultString?: string, ) => { let result; + try { result = await jsObjectIdentifier.extractExpressionAtPosition(pos); } catch (e) { diff --git a/app/client/packages/ast/src/peekOverlay/index.ts b/app/client/packages/ast/src/peekOverlay/index.ts index c9d0efec2cf..e6c51c91b32 100644 --- a/app/client/packages/ast/src/peekOverlay/index.ts +++ b/app/client/packages/ast/src/peekOverlay/index.ts @@ -14,6 +14,7 @@ export class PeekOverlayExpressionIdentifier { script?: string, ) { this.options = options; + if (script) this.updateScript(script); } @@ -64,6 +65,7 @@ export class PeekOverlayExpressionIdentifier { pos, this.options, ); + if (expressionFound) { resolve(expressionFound); } else { @@ -72,6 +74,7 @@ export class PeekOverlayExpressionIdentifier { ); } } + reject("PeekOverlayExpressionIdentifier - No node found"); }); } diff --git a/app/client/packages/ast/src/peekOverlay/utils.ts b/app/client/packages/ast/src/peekOverlay/utils.ts index 28f401f68e0..aba3349e8bb 100644 --- a/app/client/packages/ast/src/peekOverlay/utils.ts +++ b/app/client/packages/ast/src/peekOverlay/utils.ts @@ -31,6 +31,7 @@ export const getExpressionStringAtPos = ( replaceThisExpression = true, ): string | undefined => { if (!isPositionWithinNode(node, pos)) return; + if (isMemberExpressionNode(node)) { return getExpressionAtPosFromMemberExpression( node, @@ -60,12 +61,16 @@ const getExpressionAtPosFromMemberExpression = ( replaceThisExpression = true, ): string | undefined => { const objectNode = node.object; + if (isLocalVariableNode(node) || isLocalVariableNode(objectNode)) return; + if (replaceThisExpression && options?.thisExpressionReplacement) { node = replaceThisinMemberExpression(node, options); } + // stop if objectNode is a function call -> needs evaluation if (isCallExpressionNode(objectNode)) return; + // position is within the object node if (pos <= objectNode.end) { return getExpressionStringAtPos(objectNode, pos, options, false); @@ -73,6 +78,7 @@ const getExpressionAtPosFromMemberExpression = ( // position is within the property node else { const propertyNode = node.property; + if (isMemberExpressionNode(propertyNode)) { return getExpressionAtPosFromMemberExpression( propertyNode, @@ -81,6 +87,7 @@ const getExpressionAtPosFromMemberExpression = ( false, ); } + // generate string for the whole path return escodegen.generate(node); } @@ -97,6 +104,7 @@ const getExpressionAtPosFromExpressionStatement = ( ) { node.expression = thisReplacementNode(node.expression, options); } + return getExpressionStringAtPos(node.expression, pos, options); }; @@ -106,6 +114,7 @@ const getExpressionAtPosFromCallExpression = ( options?: PeekOverlayExpressionIdentifierOptions, ): string | undefined => { let selectedNode: Node | undefined; + // function call -> needs evaluation // if (isPositionWithinNode(node.callee, pos)) { // selectedNode = node.callee; @@ -114,10 +123,12 @@ const getExpressionAtPosFromCallExpression = ( const argumentNode = node.arguments.find((node) => isPositionWithinNode(node, pos), ); + if (argumentNode) { selectedNode = argumentNode; } } + return selectedNode && getExpressionStringAtPos(selectedNode, pos, options); }; @@ -127,6 +138,7 @@ const getExpressionAtPosFromConditionalExpression = ( options?: PeekOverlayExpressionIdentifierOptions, ): string | undefined => { let selectedNode: Node | undefined; + if (isPositionWithinNode(node.test, pos)) { selectedNode = node.test; } else if (isPositionWithinNode(node.consequent, pos)) { @@ -134,6 +146,7 @@ const getExpressionAtPosFromConditionalExpression = ( } else if (isPositionWithinNode(node.alternate, pos)) { selectedNode = node.alternate; } + return selectedNode && getExpressionStringAtPos(selectedNode, pos, options); }; @@ -143,11 +156,13 @@ const getExpressionAtPosFromBinaryExpression = ( options?: PeekOverlayExpressionIdentifierOptions, ): string | undefined => { let selectedNode: Node | undefined; + if (isPositionWithinNode(node.left, pos)) { selectedNode = node.left; } else if (isPositionWithinNode(node.right, pos)) { selectedNode = node.right; } + return selectedNode && getExpressionStringAtPos(selectedNode, pos, options); }; @@ -160,6 +175,7 @@ export const replaceThisinMemberExpression = ( } else if (isThisExpressionNode(node.object)) { node.object = thisReplacementNode(node.object, options); } + return node; }; diff --git a/app/client/packages/ast/src/utils.ts b/app/client/packages/ast/src/utils.ts index 6e595ebe8d8..5ac9e4489fc 100644 --- a/app/client/packages/ast/src/utils.ts +++ b/app/client/packages/ast/src/utils.ts @@ -11,6 +11,7 @@ export function sanitizeScript(js: string, evaluationVersion: number) { //default value of evalutaion version is 2 evaluationVersion = evaluationVersion ? evaluationVersion : 2; const trimmedJS = js.replace(beginsWithLineBreakRegex, ""); + return evaluationVersion > 1 ? trimmedJS : unescapeJS(trimmedJS); } @@ -52,10 +53,12 @@ export const extractContentByPosition = ( } else { returnedString += eachLine[i]; } + if (i !== position.to.line) { returnedString += "\n"; } } + return returnedString; }; @@ -67,5 +70,6 @@ export const getStringValue = ( } else if (typeof inputValue === "number" || typeof inputValue === "string") { inputValue += ""; } + return inputValue; }; diff --git a/app/client/packages/design-system/ads-old/src/AppIcon/index.tsx b/app/client/packages/design-system/ads-old/src/AppIcon/index.tsx index f5819af0804..5d532ae3d43 100644 --- a/app/client/packages/design-system/ads-old/src/AppIcon/index.tsx +++ b/app/client/packages/design-system/ads-old/src/AppIcon/index.tsx @@ -382,6 +382,7 @@ interface cssAttributes { const appSizeHandler = (size: Size): cssAttributes => { let width, height, padding; + switch (size) { case Size.small: width = 20; @@ -404,6 +405,7 @@ const appSizeHandler = (size: Size): cssAttributes => { padding = 5; break; } + return { width, height, padding }; }; @@ -436,6 +438,7 @@ function AppIcon(props: AppIconProps) { ); let returnIcon; + switch (props.name) { case "bag": returnIcon = ; @@ -708,6 +711,7 @@ function AppIcon(props: AppIconProps) { returnIcon = null; break; } + return returnIcon ? ( { useEffect(() => { const isChecked = !!intitialValue; + if (isChecked !== checked) { setChecked(isChecked); } diff --git a/app/client/packages/design-system/ads-old/src/DisplayImageUpload/Dashboard.tsx b/app/client/packages/design-system/ads-old/src/DisplayImageUpload/Dashboard.tsx index 533251f028a..a5334f999be 100644 --- a/app/client/packages/design-system/ads-old/src/DisplayImageUpload/Dashboard.tsx +++ b/app/client/packages/design-system/ads-old/src/DisplayImageUpload/Dashboard.tsx @@ -78,6 +78,7 @@ function Dashboard({ onChange(file); // TO trigger edit modal const dashboard = uppy.getPlugin("uppy-img-upload-dashboard"); + setTimeout(() => { (dashboard as any).openFileEditor(file); }); @@ -119,6 +120,7 @@ const isFileContentAnImageType = async (file: File) => { // get first 4 bytes of the file const blob = (file as any).data.slice(0, 4); const reader = new FileReader(); + reader.onloadend = () => { if (reader.result) { // convert content to a unsigned int array to read it's value @@ -126,6 +128,7 @@ const isFileContentAnImageType = async (file: File) => { const initialBytesOfFile = new Uint8Array( reader.result as ArrayBufferLike, ).reduce((prev, curr) => prev + curr.toString(16), ""); + /* compare initialBytesOfFile with magic numbers to identify file signature file signatures reference: https://en.wikipedia.org/wiki/List_of_file_signatures diff --git a/app/client/packages/design-system/ads-old/src/DisplayImageUpload/index.tsx b/app/client/packages/design-system/ads-old/src/DisplayImageUpload/index.tsx index 670c851a312..385c25b66fc 100644 --- a/app/client/packages/design-system/ads-old/src/DisplayImageUpload/index.tsx +++ b/app/client/packages/design-system/ads-old/src/DisplayImageUpload/index.tsx @@ -178,6 +178,7 @@ const SpinnerContainer = styled.div` // Dashboard is code-split away to avoid bundling Uppy in the main bundle const DashboardLazy = React.lazy(async () => { await new Promise((resolve) => setTimeout(resolve, 10000)); + return import("./Dashboard"); }); @@ -229,6 +230,7 @@ export default function DisplayImageUpload({ onClick={(e) => { e.preventDefault(); e.stopPropagation(); + if (onRemove) onRemove(); }} > diff --git a/app/client/packages/design-system/ads-old/src/DraggableList/index.tsx b/app/client/packages/design-system/ads-old/src/DraggableList/index.tsx index 681333afccf..20ec13561b9 100644 --- a/app/client/packages/design-system/ads-old/src/DraggableList/index.tsx +++ b/app/client/packages/design-system/ads-old/src/DraggableList/index.tsx @@ -148,8 +148,10 @@ export function DraggableList(props: any) { if (listRef && listRef.current && props?.distance > 0) { const containerCoordinates = listRef?.current.getBoundingClientRect(); const container = listRef.current; + if (containerCoordinates) { const containerDistanceFromTop = containerCoordinates.top; + if (props.dragging) { if (pointerFromTop < containerDistanceFromTop + itemHeight / 2) { // Scroll inside container till first element in list is completely visible @@ -170,6 +172,7 @@ export function DraggableList(props: any) { container.scrollTop += itemHeight / 10; } } + // finding distance of current pointer from the top of the container to find the final position // currIndex * itemHeight for the initial position // subtraction formar with latter for displacement @@ -208,6 +211,7 @@ export function DraggableList(props: any) { !items[curRow].isDragDisabled) ) { const newOrder = [...order.current]; + newOrder.splice(curRow, 0, newOrder.splice(curIndex, 1)[0]); setSprings( dragIdleSpringStyles(newOrder, { @@ -221,6 +225,7 @@ export function DraggableList(props: any) { itemHeight, }), ); + if (curRow !== curIndex) { // Feed springs new style data, they'll animate the view without causing a single render if (!props.down) { @@ -248,6 +253,7 @@ export function DraggableList(props: any) { } } }); + return (
{ setIsEditing(true); const errorMessage = inputValidation && inputValidation(defaultValue); + setIsInvalid(errorMessage ? errorMessage : false); e.preventDefault(); e.stopPropagation(); @@ -83,6 +84,7 @@ export function EditableText(props: EditableTextProps) { useEffect(() => { window.addEventListener("keydown", handleKeydown); + return () => { window.removeEventListener("keydown", handleKeydown); }; @@ -96,6 +98,7 @@ export function EditableText(props: EditableTextProps) { setIsEditing(true); e.preventDefault(); } + break; case "Escape": if ( @@ -104,6 +107,7 @@ export function EditableText(props: EditableTextProps) { ) ) props.wrapperRef?.current?.focus(); + break; } }; diff --git a/app/client/packages/design-system/ads-old/src/EditableTextSubComponent/index.tsx b/app/client/packages/design-system/ads-old/src/EditableTextSubComponent/index.tsx index b6ea8ed6391..e46a93020a6 100644 --- a/app/client/packages/design-system/ads-old/src/EditableTextSubComponent/index.tsx +++ b/app/client/packages/design-system/ads-old/src/EditableTextSubComponent/index.tsx @@ -172,18 +172,23 @@ export const EditableTextSubComponent = React.forwardRef( const onConfirm = useCallback( (_value: string) => { const finalVal: string = _value.trim(); + onBlurEverytime && onBlurEverytime(finalVal); + if (savingState === SavingState.ERROR || isInvalid || finalVal === "") { setValue(lastValidValue); onBlur && onBlur(lastValidValue); setSavingState(SavingState.NOT_STARTED); } + if (changeStarted) { onTextChanged && onTextChanged(finalVal); } + if (finalVal && finalVal !== defaultValue) { onBlur && onBlur(finalVal); } + setIsEditing(false); setChangeStarted(false); }, @@ -201,15 +206,19 @@ export const EditableTextSubComponent = React.forwardRef( (_value: string) => { let finalVal: string = _value.indexOf(" ") === 0 ? _value.trim() : _value; + if (valueTransform) { finalVal = valueTransform(finalVal); } + const errorMessage = inputValidation && inputValidation(finalVal); const error = errorMessage ? errorMessage : false; + if (!error && finalVal !== "") { setLastValidValue(finalVal); onTextChanged && onTextChanged(finalVal); } + setValue(finalVal); setIsInvalid(error); setChangeStarted(true); diff --git a/app/client/packages/design-system/ads-old/src/FilePickerV2/index.tsx b/app/client/packages/design-system/ads-old/src/FilePickerV2/index.tsx index f1b22282ecc..f6809127d6b 100644 --- a/app/client/packages/design-system/ads-old/src/FilePickerV2/index.tsx +++ b/app/client/packages/design-system/ads-old/src/FilePickerV2/index.tsx @@ -251,6 +251,7 @@ function FilePickerComponent(props: FilePickerProps) { function ButtonClick(event: React.MouseEvent) { event.preventDefault(); + if (inputRef.current) { inputRef.current.click(); } @@ -259,9 +260,11 @@ function FilePickerComponent(props: FilePickerProps) { function onDrop(monitor: DropTargetMonitor) { if (monitor) { const files = monitor.getItem().files; + if (!files) { return; } + handleFileUpload(files); } } @@ -270,8 +273,10 @@ function FilePickerComponent(props: FilePickerProps) { if (progressRef.current) { progressRef.current.style.width = `${uploadPercentage}%`; } + if (uploadPercentage === 100) { setIsUploaded(true); + if (fileDescRef.current && bgRef.current && fileType === FileType.IMAGE) { fileDescRef.current.style.display = "none"; bgRef.current.style.opacity = "1"; @@ -294,21 +299,27 @@ function FilePickerComponent(props: FilePickerProps) { function handleOtherFileUpload(files: FileList | null) { const file = files && files[0]; let fileSize = 0; + if (!file) { return; } + fileSize = Math.floor(file.size / 1024); setFileInfo({ name: file.name, size: fileSize }); + if (props.delayedUpload) { setIsUploaded(true); setProgress(100); } + if (fileDescRef.current) { fileDescRef.current.style.display = "flex"; } + if (fileContainerRef.current) { fileContainerRef.current.style.display = "none"; } + fileUploader && fileUploader(file, setProgress, onUpload); } @@ -319,6 +330,7 @@ function FilePickerComponent(props: FilePickerProps) { if (!file) { return; } + fileSize = Math.floor(file.size / 1024); setFileInfo({ name: file.name, size: fileSize }); @@ -329,9 +341,11 @@ function FilePickerComponent(props: FilePickerProps) { )})`; bgRef.current.style.opacity = "0.5"; } + if (fileDescRef.current) { fileDescRef.current.style.display = "block"; } + if (fileContainerRef.current) { fileContainerRef.current.style.display = "none"; } @@ -348,13 +362,17 @@ function FilePickerComponent(props: FilePickerProps) { function removeFile() { if (fileContainerRef.current) { setFileUrl(""); + if (fileDescRef.current) { fileDescRef.current.style.display = "none"; } + fileContainerRef.current.style.display = "flex"; + if (bgRef.current) { bgRef.current.style.backgroundImage = "url('')"; } + setIsUploaded(false); onFileRemoved && onFileRemoved(); } @@ -365,6 +383,7 @@ function FilePickerComponent(props: FilePickerProps) { useEffect(() => { if (props.url) { const urlKeys = props.url.split("/"); + if (urlKeys[urlKeys.length - 1] !== "null") { setFileUrl(props.url); } else { @@ -377,13 +396,16 @@ function FilePickerComponent(props: FilePickerProps) { useEffect(() => { if (fileUrl && !isUploaded && fileType === FileType.IMAGE) { setIsUploaded(true); + if (bgRef.current) { bgRef.current.style.backgroundImage = `url(${fileUrl})`; bgRef.current.style.opacity = "1"; } + if (fileDescRef.current) { fileDescRef.current.style.display = "none"; } + if (fileContainerRef.current) { fileContainerRef.current.style.display = "none"; } diff --git a/app/client/packages/design-system/ads-old/src/GifPlayer/index.tsx b/app/client/packages/design-system/ads-old/src/GifPlayer/index.tsx index 7c0b7e81fc8..8d1d1695503 100644 --- a/app/client/packages/design-system/ads-old/src/GifPlayer/index.tsx +++ b/app/client/packages/design-system/ads-old/src/GifPlayer/index.tsx @@ -55,6 +55,7 @@ const Overlay = styled.div` function GifPlayer(props: GifPlayerProps) { const [startGif, setStartGif] = useState(false); + return !startGif ? ( setStartGif(!startGif)}> diff --git a/app/client/packages/design-system/ads-old/src/HighlightText/index.tsx b/app/client/packages/design-system/ads-old/src/HighlightText/index.tsx index cd1ab376f4b..fcd4409529b 100644 --- a/app/client/packages/design-system/ads-old/src/HighlightText/index.tsx +++ b/app/client/packages/design-system/ads-old/src/HighlightText/index.tsx @@ -14,6 +14,7 @@ export type HighlightTextProps = { export function HighlightText(props: HighlightTextProps) { const { highlight = "", text = "", ...rest } = props; + if (!highlight.trim()) { return ( @@ -21,6 +22,7 @@ export function HighlightText(props: HighlightTextProps) { ); } + const regex = new RegExp(`(${escapeRegExp(highlight)})`, "gi"); const parts: string[] = text.split(regex); diff --git a/app/client/packages/design-system/ads-old/src/IconSelector/index.tsx b/app/client/packages/design-system/ads-old/src/IconSelector/index.tsx index d24bb6f305b..8d9bd5eff06 100644 --- a/app/client/packages/design-system/ads-old/src/IconSelector/index.tsx +++ b/app/client/packages/design-system/ads-old/src/IconSelector/index.tsx @@ -66,10 +66,12 @@ function IconSelector(props: IconSelectorProps) { iconPalette[0] === props.iconPalette[0] ) { const _iconPalette = iconPalette ? [...iconPalette] : []; + _iconPalette?.splice(_iconPalette.indexOf(props.selectedIcon), 1); _iconPalette?.splice(0, 0, props.selectedIcon); setIconPalette(_iconPalette); } + // icon position change ends here setSelected(props.selectedIcon); } @@ -79,6 +81,7 @@ function IconSelector(props: IconSelectorProps) { if (props.iconPalette && props.iconPalette[0]) { return props.iconPalette[0]; } + return AppIconCollection[0]; } diff --git a/app/client/packages/design-system/ads-old/src/MenuItem/index.tsx b/app/client/packages/design-system/ads-old/src/MenuItem/index.tsx index ffe22f6815e..0d30e6afab1 100644 --- a/app/client/packages/design-system/ads-old/src/MenuItem/index.tsx +++ b/app/client/packages/design-system/ads-old/src/MenuItem/index.tsx @@ -85,6 +85,7 @@ const MenuItem = forwardRef( const MenuItemContent = forwardRef( (props: MenuItemProps, ref: Ref) => { const { onSelect } = props; + return ( ); } + export default ProgressiveImage; diff --git a/app/client/packages/design-system/ads-old/src/RectangularSwitcher/index.tsx b/app/client/packages/design-system/ads-old/src/RectangularSwitcher/index.tsx index 681565adf00..506037902e9 100644 --- a/app/client/packages/design-system/ads-old/src/RectangularSwitcher/index.tsx +++ b/app/client/packages/design-system/ads-old/src/RectangularSwitcher/index.tsx @@ -117,6 +117,7 @@ export default function Switch(props: SwitchProps) { if (!firstRender) { setFirstRender(true); } + onChangeHandler(e.target.checked); }} type="checkbox" diff --git a/app/client/packages/design-system/ads-old/src/SearchComponent/index.tsx b/app/client/packages/design-system/ads-old/src/SearchComponent/index.tsx index 988a5bd9156..ab51942ff46 100644 --- a/app/client/packages/design-system/ads-old/src/SearchComponent/index.tsx +++ b/app/client/packages/design-system/ads-old/src/SearchComponent/index.tsx @@ -106,6 +106,7 @@ class SearchComponent extends React.Component< | React.ChangeEvent, ) => { const search = event.target.value; + this.setState({ localValue: search }); this.onDebouncedSearch(search); }; diff --git a/app/client/packages/design-system/ads-old/src/Statusbar/index.tsx b/app/client/packages/design-system/ads-old/src/Statusbar/index.tsx index e8e8bd648e9..8026cbeb15a 100644 --- a/app/client/packages/design-system/ads-old/src/Statusbar/index.tsx +++ b/app/client/packages/design-system/ads-old/src/Statusbar/index.tsx @@ -86,6 +86,7 @@ export default function OnboardingStatusbar(props: StatusbarProps) { const displayMessage = showOnlyMessage ? message : `${percentage}% ${message}`; + return ( 0 ? ( rows.map((row, index) => { prepareRow(row); + return ( {row.cells.map((cell, index) => { diff --git a/app/client/packages/design-system/ads-old/src/TagInput/index.tsx b/app/client/packages/design-system/ads-old/src/TagInput/index.tsx index 0416aa9a6fa..9076aa65170 100644 --- a/app/client/packages/design-system/ads-old/src/TagInput/index.tsx +++ b/app/client/packages/design-system/ads-old/src/TagInput/index.tsx @@ -9,6 +9,7 @@ import { export const isEmail = (value: string) => { const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(value); }; @@ -127,6 +128,7 @@ function TagInputComponent(props: TagInputProps) { if (inputValues.length === 0 && values.length > 0) { setValues([]); } + if (inputValues.length > 0 && values.length === 0) { setValues(inputValues); } @@ -139,6 +141,7 @@ function TagInputComponent(props: TagInputProps) { const validateEmail = (newValues: string[]) => { if (newValues && newValues.length > 0) { let error = ""; + newValues.forEach((user: any) => { if (!isEmail(user)) { error = createMessage(INVITE_USERS_VALIDATION_EMAIL_LIST); @@ -159,6 +162,7 @@ function TagInputComponent(props: TagInputProps) { const onTagsChange = (values: React.ReactNode[]) => { const _values = values as string[]; + commitValues(_values); }; @@ -173,12 +177,14 @@ function TagInputComponent(props: TagInputProps) { e.target.value ) { const newValues = [...values, e.target.value]; + commitValues(newValues); setCurrentValue(""); e.preventDefault(); } else if (e.key === "Backspace") { if (e.target.value.length === 0) { const newValues = values.slice(0, -1); + commitValues(newValues); } } @@ -197,6 +203,7 @@ function TagInputComponent(props: TagInputProps) { const handleInputBlur = (e: any) => { if (e?.target?.value?.trim() || isEmail(e.target.value)) { const newValues = [...values, e.target.value]; + commitValues(newValues); setCurrentValue(""); e.preventDefault(); diff --git a/app/client/packages/design-system/ads-old/src/Text/index.tsx b/app/client/packages/design-system/ads-old/src/Text/index.tsx index e650c814398..7e000e03801 100644 --- a/app/client/packages/design-system/ads-old/src/Text/index.tsx +++ b/app/client/packages/design-system/ads-old/src/Text/index.tsx @@ -46,6 +46,7 @@ export type TextProps = CommonComponentProps & { const typeSelector = (props: TextProps): string => { let color = ""; + switch (props.type) { case TextType.P0: color = "var(--ads-v2-color-fg)"; @@ -63,6 +64,7 @@ const typeSelector = (props: TextProps): string => { color = "var(--ads-v2-color-fg-emphasis-plus)"; break; } + return color; }; diff --git a/app/client/packages/design-system/ads-old/src/Tooltip/index.tsx b/app/client/packages/design-system/ads-old/src/Tooltip/index.tsx index 85058468717..998f382cf77 100644 --- a/app/client/packages/design-system/ads-old/src/Tooltip/index.tsx +++ b/app/client/packages/design-system/ads-old/src/Tooltip/index.tsx @@ -70,6 +70,7 @@ const TooltipWrapper = styled(Tooltip)< if (!portalContainer) { const tooltipPortalElement = document.createElement("div"); + tooltipPortalElement.id = rootElementId; document.body.append(tooltipPortalElement); portalContainer = document.getElementById(rootElementId); diff --git a/app/client/packages/design-system/ads-old/src/TreeDropdown/index.tsx b/app/client/packages/design-system/ads-old/src/TreeDropdown/index.tsx index 0a5c6a3322e..120734ddfb8 100644 --- a/app/client/packages/design-system/ads-old/src/TreeDropdown/index.tsx +++ b/app/client/packages/design-system/ads-old/src/TreeDropdown/index.tsx @@ -194,8 +194,10 @@ export function calculateNext(arr: number[], max: number) { export function calculatePrev(arr: number[], max: number) { let lastNum = arr[arr.length - 1]; + if (lastNum <= 0) lastNum = max; else lastNum--; + return [...arr.slice(0, arr.length - 1), lastNum]; } @@ -204,8 +206,11 @@ export function getItem( index: number[], ): TreeDropdownOption | undefined { if (index.length === 0) return undefined; + const firstIndex = index[0] ?? 0; + if (index.length === 1) return arr[firstIndex]; + return getItem(arr[firstIndex]?.children ?? [], index.slice(1)); } @@ -215,9 +220,11 @@ export function setItem( item: TreeDropdownOption, ): TreeDropdownOption[] | undefined { if (index.length === 0) return undefined; + const firstIndex = index[0] ?? 0; let subItem = { ...arr[firstIndex] }; + if (subItem.children && index.length > 1) subItem.children = setItem(subItem.children, index.slice(1), item); else subItem = item; @@ -228,12 +235,15 @@ export function setItem( export function closeAllChildren(tree: TreeDropdownOption[]) { return tree.map((x) => { let data = x; + if (x.isChildrenOpen) data = { ...x, isChildrenOpen: false, }; + if (x.children) data["children"] = closeAllChildren(x.children); + return data; }); } @@ -241,10 +251,14 @@ export function closeAllChildren(tree: TreeDropdownOption[]) { export function deepOpenChildren(tree: TreeDropdownOption[], index: number[]) { return tree.map((x, i) => { if (i !== index[0]) return x; + const data = x; + data["isChildrenOpen"] = true; + if (x?.children) data["children"] = deepOpenChildren(data?.children ?? [], index.slice(1)); + return x; }); } @@ -255,8 +269,11 @@ export function setSelfIndex( ): TreeDropdownOption[] { return tree.map((x, i) => { const ob: any = { ...x }; + ob.selfIndex = [...prevIndex, i]; + if (ob.children) ob.children = setSelfIndex(ob.children, ob.selfIndex); + return ob; }); } @@ -270,6 +287,7 @@ function getSelectedOption( label: defaultText, value: "", }; + options.length > 0 && options.forEach((option) => { // Find the selected option in the OptionsTree @@ -279,11 +297,13 @@ function getSelectedOption( const childOption = find(option.children, { value: selectedValue, }); + if (childOption) { selectedOption = childOption; } } }); + return selectedOption; } @@ -407,8 +427,10 @@ function TreeDropdown(props: TreeDropdownProps) { defaultText, optionTree, ); + setSelectedOption((prev) => { if (prev.value === defaultSelectedOption.value) return prev; + return defaultSelectedOption; }); } @@ -431,8 +453,10 @@ function TreeDropdown(props: TreeDropdownProps) { option.onSelect(option, onSelect); } else { const defaultVal = getDefaults ? getDefaults(option.value) : undefined; + onSelect(option, defaultVal, isUpdatedViaKeyboard); } + setSelectedOption(option); }; @@ -440,6 +464,7 @@ function TreeDropdown(props: TreeDropdownProps) { if (option.children) return (e: any) => { const itemIndex = option.selfIndex || []; + if (option?.children) { setOptionTree((prev) => { if (option.isChildrenOpen) @@ -453,15 +478,19 @@ function TreeDropdown(props: TreeDropdownProps) { }, ) ?? prev ); + return deepOpenChildren(closeAllChildren(prev), itemIndex); }); buttonRef.current?.focus(); setSelectedOption(option.children[0]); + if (option?.children[0]?.selfIndex) selectedOptionIndex.current = option.children[0].selfIndex; } + e?.stopPropagation && e.stopPropagation(); }; + return (e: any, isUpdatedViaKeyboard = false) => { handleSelect(option, isUpdatedViaKeyboard); setIsOpen(false); @@ -482,13 +511,16 @@ function TreeDropdown(props: TreeDropdownProps) { case "Escape": if (isOpen) { emitKeyPressEvent(e.key); + if (selectedOptionIndex.current.length > 1) { setOptionTree((prev) => { const prevIndex = selectedOptionIndex.current.slice(0, -1); const prevItem = getItem(prev, prevIndex); + if (prevItem) { selectedOptionIndex.current = prevIndex; setSelectedOption(prevItem); + return ( setItem(prev, prevIndex, { ...prevItem, @@ -496,13 +528,16 @@ function TreeDropdown(props: TreeDropdownProps) { }) ?? prev ); } + return prev; }); } else { setIsOpen(false); } + e.nativeEvent.stopImmediatePropagation(); } + break; case " ": case "Enter": @@ -510,6 +545,7 @@ function TreeDropdown(props: TreeDropdownProps) { if (isOpen) { emitKeyPressEvent(e.key); const selectedOpt = getItem(optionTree, selectedOptionIndex.current); + if (selectedOpt?.children) { handleOptionClick(selectedOpt)(e, true); } else if (selectedOpt && e.key !== "ArrowRight") { @@ -522,17 +558,21 @@ function TreeDropdown(props: TreeDropdownProps) { selectedOptionIndex.current = [findIndex(optionTree, selectedOption)]; shouldOpen.current = true; } + break; case "ArrowUp": emitKeyPressEvent(e.key); e.preventDefault(); + if (isOpen) { let currentLength = optionTree.length; + if (selectedOptionIndex.current.length > 1) { currentLength = getItem(optionTree, selectedOptionIndex.current.slice(0, -1)) ?.children?.length ?? 0; } + selectedOptionIndex.current = calculatePrev( selectedOptionIndex.current, currentLength - 1, @@ -540,21 +580,26 @@ function TreeDropdown(props: TreeDropdownProps) { const nextItem = getItem(optionTree, selectedOptionIndex.current) ?? getSelectedOption(selectedValue, defaultText, optionTree); + setSelectedOption(nextItem); } else { setIsOpen(true); } + break; case "ArrowDown": emitKeyPressEvent(e.key); e.preventDefault(); + if (isOpen) { let currentLength = optionTree.length; + if (selectedOptionIndex.current.length > 1) { currentLength = getItem(optionTree, selectedOptionIndex.current.slice(0, -1)) ?.children?.length ?? 0; } + selectedOptionIndex.current = calculateNext( selectedOptionIndex.current, currentLength - 1, @@ -562,13 +607,16 @@ function TreeDropdown(props: TreeDropdownProps) { const nextItem = getItem(optionTree, selectedOptionIndex.current) ?? getSelectedOption(selectedValue, defaultText, optionTree); + setSelectedOption(nextItem); } else { setIsOpen(true); } + break; case "Tab": emitKeyPressEvent(`${e.shiftKey ? "Shift+" : ""}${e.key}`); + if (isOpen) { setIsOpen(false); // reset selected option @@ -576,16 +624,20 @@ function TreeDropdown(props: TreeDropdownProps) { getSelectedOption(selectedValue, defaultText, optionTree), ); } + break; case "ArrowLeft": emitKeyPressEvent(e.key); + if (selectedOptionIndex.current.length > 1) { setOptionTree((prev) => { const prevIndex = selectedOptionIndex.current.slice(0, -1); const prevItem = getItem(prev, prevIndex); + if (prevItem) { selectedOptionIndex.current = prevIndex; setSelectedOption(prevItem); + return ( setItem(prev, prevIndex, { ...prevItem, @@ -593,9 +645,11 @@ function TreeDropdown(props: TreeDropdownProps) { }) ?? prev ); } + return prev; }); } + break; } }; @@ -635,6 +689,7 @@ function TreeDropdown(props: TreeDropdownProps) { /> ); + return ( { // e.detail will be 1 if the event is a mouse click if (e.detail === 1) shouldOpen.current = true; + if (shouldOpen.current) setIsOpen(true); + props.onMenuToggle && props.onMenuToggle(true); e.stopPropagation(); }, diff --git a/app/client/packages/design-system/ads-old/src/hooks/useResizeObserver.ts b/app/client/packages/design-system/ads-old/src/hooks/useResizeObserver.ts index a34fc62320d..b7f2b512110 100644 --- a/app/client/packages/design-system/ads-old/src/hooks/useResizeObserver.ts +++ b/app/client/packages/design-system/ads-old/src/hooks/useResizeObserver.ts @@ -10,6 +10,7 @@ const useResizeObserver = ( const resizeObserver = new ResizeObserver((entries: any) => { callback(entries); }); + resizeObserver.observe(ref); return () => resizeObserver.unobserve(ref); diff --git a/app/client/packages/design-system/ads-old/src/utils/colors.ts b/app/client/packages/design-system/ads-old/src/utils/colors.ts index 1d9d2ee2657..0f2220a6b36 100644 --- a/app/client/packages/design-system/ads-old/src/utils/colors.ts +++ b/app/client/packages/design-system/ads-old/src/utils/colors.ts @@ -7,6 +7,7 @@ function validateHex(arg: string) { if (regex.test(arg)) return arg; else { const cssVariable = arg.substring(4, arg.length - 1); + return getComputedStyle(document.documentElement).getPropertyValue( cssVariable, ); @@ -22,6 +23,7 @@ export const hexToRgb = ( } => { const validatedHex = validateHex(hex); const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(validatedHex); + return result ? { r: parseInt(result[1], 16), @@ -36,6 +38,7 @@ export const hexToRgb = ( }; export const hexToRgba = (color: string, alpha: number) => { const value = hexToRgb(color); + return `rgba(${value.r}, ${value.g}, ${value.b}, ${alpha});`; }; export const lighten = (color: string, amount: number) => { diff --git a/app/client/packages/design-system/ads-old/src/utils/emailValidator.ts b/app/client/packages/design-system/ads-old/src/utils/emailValidator.ts index 311dda4e13b..39022b8eda3 100644 --- a/app/client/packages/design-system/ads-old/src/utils/emailValidator.ts +++ b/app/client/packages/design-system/ads-old/src/utils/emailValidator.ts @@ -6,14 +6,17 @@ import { const isEmail = (value: string) => { const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(value); }; export function emailValidator(email: string) { let isValid = true; + if (email) { isValid = isEmail(email); } + return { isValid: isValid, message: !isValid ? createMessage(FORM_VALIDATION_INVALID_EMAIL) : "", diff --git a/app/client/packages/design-system/ads-old/src/utils/icon-loadables.tsx b/app/client/packages/design-system/ads-old/src/utils/icon-loadables.tsx index fcbba238d45..f3c3f2d4f4c 100644 --- a/app/client/packages/design-system/ads-old/src/utils/icon-loadables.tsx +++ b/app/client/packages/design-system/ads-old/src/utils/icon-loadables.tsx @@ -38,6 +38,7 @@ export function importSvg( .then((m) => ({ default: m.ReactComponent })) .catch((e) => { log.warn("Failed to load SVG icon:", e); + return { default: IconLoadFailFallback }; }), ); @@ -49,6 +50,7 @@ export function importRemixIcon( return importIconImpl(async () => importFn().catch((e) => { log.warn("Failed to load SVG icon:", e); + return { default: IconLoadFailFallback }; }), ); diff --git a/app/client/packages/design-system/ads-old/src/utils/notEmptyValidator.ts b/app/client/packages/design-system/ads-old/src/utils/notEmptyValidator.ts index 8eb24659943..65e991aad66 100644 --- a/app/client/packages/design-system/ads-old/src/utils/notEmptyValidator.ts +++ b/app/client/packages/design-system/ads-old/src/utils/notEmptyValidator.ts @@ -2,6 +2,7 @@ import { createMessage, ERROR_MESSAGE_NAME_EMPTY } from "../constants/messages"; export function notEmptyValidator(value: string) { const isValid = !!value; + return { isValid: isValid, message: !isValid ? createMessage(ERROR_MESSAGE_NAME_EMPTY) : "", diff --git a/app/client/packages/design-system/ads/src/AnnouncementPopover/AnnouncementPopover.tsx b/app/client/packages/design-system/ads/src/AnnouncementPopover/AnnouncementPopover.tsx index fef245fc800..697c1d8a1ed 100644 --- a/app/client/packages/design-system/ads/src/AnnouncementPopover/AnnouncementPopover.tsx +++ b/app/client/packages/design-system/ads/src/AnnouncementPopover/AnnouncementPopover.tsx @@ -25,6 +25,7 @@ import { import { Button } from "../Button"; const AnnouncementPopover = HoverCard.Root; + AnnouncementPopover.displayName = "AnnouncementPopover"; function AnnouncementPopoverTrigger(props: HoverCard.HoverCardTriggerProps) { diff --git a/app/client/packages/design-system/ads/src/Avatar/Avatar.tsx b/app/client/packages/design-system/ads/src/Avatar/Avatar.tsx index 7f9d701744f..35c568102b6 100644 --- a/app/client/packages/design-system/ads/src/Avatar/Avatar.tsx +++ b/app/client/packages/design-system/ads/src/Avatar/Avatar.tsx @@ -112,6 +112,7 @@ function AvatarGroup(props: AvatarGroupProps) { } = props; const mainAvatars = avatars.slice(0, maxAvatars); const restAvatars = avatars.slice(maxAvatars); + return ( {mainAvatars.map((avatar: AvatarGroupAvatarProps, index: number) => { diff --git a/app/client/packages/design-system/ads/src/Box/Box.tsx b/app/client/packages/design-system/ads/src/Box/Box.tsx index d9fa972e826..3d13e7a776f 100644 --- a/app/client/packages/design-system/ads/src/Box/Box.tsx +++ b/app/client/packages/design-system/ads/src/Box/Box.tsx @@ -17,6 +17,7 @@ function Box({ children, className, ...rest }: BoxProps) { [key]: newValue, }; }, {} as BoxProps); + return ( {children} diff --git a/app/client/packages/design-system/ads/src/Button/Button.styles.tsx b/app/client/packages/design-system/ads/src/Button/Button.styles.tsx index 19252abfda6..b5e58e42b28 100644 --- a/app/client/packages/design-system/ads/src/Button/Button.styles.tsx +++ b/app/client/packages/design-system/ads/src/Button/Button.styles.tsx @@ -39,6 +39,7 @@ const getSizes = (size: ButtonSizes, isIconButton?: boolean) => { --button-gap: var(--ads-v2-spaces-3); `, }; + return Sizes[size]; }; @@ -56,6 +57,7 @@ const getHeights = (size: ButtonSizes, isIconButton?: boolean) => { // --button-height: 40px; // `, }; + return Heights[size]; }; diff --git a/app/client/packages/design-system/ads/src/Button/Button.tsx b/app/client/packages/design-system/ads/src/Button/Button.tsx index 7a62dfee77a..ca98873225e 100644 --- a/app/client/packages/design-system/ads/src/Button/Button.tsx +++ b/app/client/packages/design-system/ads/src/Button/Button.tsx @@ -36,10 +36,12 @@ const Button = forwardRef( UNSAFE_width, ...rest } = props; + // disable button when loading rest.onClick = props.isLoading || props.isDisabled ? undefined : props.onClick; const buttonRef = useDOMRef(ref); + return ( {links.map((link) => { const { endIcon, onClick, startIcon, to, ...restOfLink } = link; + return ( { setSelectedDate(date); onChange && onChange(date, e); + if (e) { setIsOpen(false); } @@ -179,8 +180,10 @@ export function createDefaultShortcuts( const today = new Date(); const makeDate = (action: (d: Date) => void) => { const returnVal = clone(today); + action(returnVal); returnVal.setDate(returnVal.getDate() + 1); + return returnVal; }; @@ -261,6 +264,7 @@ function DateRangeShortcuts(props: DateRangeShortcutsProps) { const [selectedShortCut, setSelectedShortCut] = useState< DateRangeShortcut | undefined >(); + useEffect(() => { if (currentDates) { const currentSelectedShortcut = shortCuts.find( @@ -269,9 +273,11 @@ function DateRangeShortcuts(props: DateRangeShortcutsProps) { currentDates[0]?.toDateString() && each.dateRange[1]?.toDateString() === currentDates[1]?.toDateString(), ); + setSelectedShortCut(currentSelectedShortcut); } }, [currentDates]); + return showRangeShortcuts ? ( @@ -280,6 +286,7 @@ function DateRangeShortcuts(props: DateRangeShortcutsProps) { onChangeHandler(each.dateRange, e, "shortcut"); }; const isSelected = selectedShortCut?.label === each.label; + return ( { const year = monthDate.getFullYear(); + if (year !== selectedYear) { setSelectedYear(year); } @@ -473,10 +481,12 @@ function DateRangePicker( const [isOpen, setIsOpen] = useState(false); const [showPreviousMonthsState, setShowPreviousMonths] = useState(showPreviousMonths); + useEffect(() => { if (propStartDate !== startDate) { setStartDate(propStartDate || null); } + if (propEndDate !== endDate) { setEndDate(propEndDate || null); } @@ -488,12 +498,15 @@ function DateRangePicker( type?: string, ) => { const [startDate, endDate] = date; + setStartDate(startDate); setEndDate(endDate); onChange && onChange(date, e); + if (type === "shortcut") { setIsOpen(false); } + if (showPreviousMonths) { // doing this to avoid janky behaviour when navigating through the datepicker. setShowPreviousMonths(false); diff --git a/app/client/packages/design-system/ads/src/Divider/Divider.test.tsx b/app/client/packages/design-system/ads/src/Divider/Divider.test.tsx index 60514b538ac..626f5791091 100644 --- a/app/client/packages/design-system/ads/src/Divider/Divider.test.tsx +++ b/app/client/packages/design-system/ads/src/Divider/Divider.test.tsx @@ -7,6 +7,7 @@ describe("Divider", () => { const { getByTestId } = render(); // eslint-disable-next-line testing-library/prefer-screen-queries const divider = getByTestId("divider"); + expect(divider).toBeInTheDocument(); }); @@ -14,6 +15,7 @@ describe("Divider", () => { const { getByTestId } = render(); // eslint-disable-next-line testing-library/prefer-screen-queries const divider = getByTestId("divider"); + expect(divider).toHaveAttribute("orientation", "horizontal"); }); @@ -23,6 +25,7 @@ describe("Divider", () => { ); // eslint-disable-next-line testing-library/prefer-screen-queries const divider = getByTestId("divider"); + expect(divider).not.toHaveAttribute("orientation", "horizontal"); expect(divider).toHaveAttribute("orientation", "vertical"); }); diff --git a/app/client/packages/design-system/ads/src/Documentation/components/ColorBlock.tsx b/app/client/packages/design-system/ads/src/Documentation/components/ColorBlock.tsx index 52ad545f35f..4de736f5c21 100644 --- a/app/client/packages/design-system/ads/src/Documentation/components/ColorBlock.tsx +++ b/app/client/packages/design-system/ads/src/Documentation/components/ColorBlock.tsx @@ -9,6 +9,7 @@ function ColorBlock({ color }: ColorBlockProps) { const colorHex = getComputedStyle(document.documentElement).getPropertyValue( color, ); + return ( diff --git a/app/client/packages/design-system/ads/src/FormControl/FormControl.tsx b/app/client/packages/design-system/ads/src/FormControl/FormControl.tsx index d3257058bc0..771391193fa 100644 --- a/app/client/packages/design-system/ads/src/FormControl/FormControl.tsx +++ b/app/client/packages/design-system/ads/src/FormControl/FormControl.tsx @@ -56,6 +56,7 @@ FormControl.displayName = "FormControl"; function FormLabel({ children, className, ...rest }: FormLabelProps) { const { isRequired, size } = useFormControlContext(); + return ( ; } diff --git a/app/client/packages/design-system/ads/src/Icon/Icon.test.tsx b/app/client/packages/design-system/ads/src/Icon/Icon.test.tsx index 718127567e2..6dc51710cc6 100644 --- a/app/client/packages/design-system/ads/src/Icon/Icon.test.tsx +++ b/app/client/packages/design-system/ads/src/Icon/Icon.test.tsx @@ -30,6 +30,7 @@ describe("Icon Component", () => { ); // eslint-disable-next-line testing-library/prefer-screen-queries const icon = getByTestId(IconClassName); + // eslint-disable-next-line testing-library/no-node-access expect(icon.firstChild).toBe(null); }); diff --git a/app/client/packages/design-system/ads/src/Icon/loadables.tsx b/app/client/packages/design-system/ads/src/Icon/loadables.tsx index fcbba238d45..f3c3f2d4f4c 100644 --- a/app/client/packages/design-system/ads/src/Icon/loadables.tsx +++ b/app/client/packages/design-system/ads/src/Icon/loadables.tsx @@ -38,6 +38,7 @@ export function importSvg( .then((m) => ({ default: m.ReactComponent })) .catch((e) => { log.warn("Failed to load SVG icon:", e); + return { default: IconLoadFailFallback }; }), ); @@ -49,6 +50,7 @@ export function importRemixIcon( return importIconImpl(async () => importFn().catch((e) => { log.warn("Failed to load SVG icon:", e); + return { default: IconLoadFailFallback }; }), ); diff --git a/app/client/packages/design-system/ads/src/Link/Link.styles.tsx b/app/client/packages/design-system/ads/src/Link/Link.styles.tsx index e194ad91eaa..357316fd4d7 100644 --- a/app/client/packages/design-system/ads/src/Link/Link.styles.tsx +++ b/app/client/packages/design-system/ads/src/Link/Link.styles.tsx @@ -71,6 +71,7 @@ const Kind = { } `, }; + export const Styles = css<{ kind?: LinkKind }>` ${Variables} diff --git a/app/client/packages/design-system/ads/src/Link/Link.test.tsx b/app/client/packages/design-system/ads/src/Link/Link.test.tsx index d310958424c..3585617e33c 100644 --- a/app/client/packages/design-system/ads/src/Link/Link.test.tsx +++ b/app/client/packages/design-system/ads/src/Link/Link.test.tsx @@ -15,6 +15,7 @@ describe("Link component", () => { ); // eslint-disable-next-line testing-library/prefer-screen-queries const link = getByTestId(LinkClassName); + expect(link).toBeInTheDocument(); expect(link.getAttribute("href")).toBe("/old"); }); @@ -27,6 +28,7 @@ describe("Link component", () => { ); // eslint-disable-next-line testing-library/prefer-screen-queries const link = getByTestId(LinkClassName); + expect(link).toBeInTheDocument(); expect(link.getAttribute("href")).toBe("https://appsmith.com"); }); diff --git a/app/client/packages/design-system/ads/src/Link/Link.tsx b/app/client/packages/design-system/ads/src/Link/Link.tsx index faf6ee182e9..ae13c6ec06b 100644 --- a/app/client/packages/design-system/ads/src/Link/Link.tsx +++ b/app/client/packages/design-system/ads/src/Link/Link.tsx @@ -32,6 +32,7 @@ function Link(props: LinkProps) { if (url.indexOf("//") === 0) { url = location.protocol + url; } + return url .toLowerCase() .replace(/([a-z])?:\/\//, "$1") @@ -79,6 +80,7 @@ function Link(props: LinkProps) { if (!rest.to || rest.to === "") { e.preventDefault(); } + rest.onClick?.(e); }, } diff --git a/app/client/packages/design-system/ads/src/List/List.stories.tsx b/app/client/packages/design-system/ads/src/List/List.stories.tsx index 90f21cabc6b..160b027f7d8 100644 --- a/app/client/packages/design-system/ads/src/List/List.stories.tsx +++ b/app/client/packages/design-system/ads/src/List/List.stories.tsx @@ -153,6 +153,7 @@ const ListItemArgTypes = { function ListItemTemplate(args: JSX.IntrinsicAttributes & ListItemProps) { return ; } + export const ListItemLargeStory = ListItemTemplate.bind({}) as StoryObj; ListItemLargeStory.storyName = "List item size large"; ListItemLargeStory.argTypes = ListItemArgTypes; diff --git a/app/client/packages/design-system/ads/src/List/List.tsx b/app/client/packages/design-system/ads/src/List/List.tsx index 03ac14c491b..c4761a73972 100644 --- a/app/client/packages/design-system/ads/src/List/List.tsx +++ b/app/client/packages/design-system/ads/src/List/List.tsx @@ -42,8 +42,10 @@ function TextWithTooltip(props: TextProps & { isMultiline?: boolean }) { const isEllipsisActive = () => { let active = false; + if (ref.current) { const text_node = ref.current.children[0]; + if (props.isMultiline) { active = text_node && text_node.clientHeight < text_node.scrollHeight; } else { diff --git a/app/client/packages/design-system/ads/src/Menu/Menu.tsx b/app/client/packages/design-system/ads/src/Menu/Menu.tsx index 67fe93cdeb4..0664f45a686 100644 --- a/app/client/packages/design-system/ads/src/Menu/Menu.tsx +++ b/app/client/packages/design-system/ads/src/Menu/Menu.tsx @@ -89,6 +89,7 @@ function MenuTrigger({ function MenuItemContent(props: MenuItemContentProps) { const { children, endIcon, size = "md", startIcon } = props; + return ( <> {startIcon && ( @@ -132,6 +133,7 @@ const MenuItem = React.forwardRef( ); }, ); + MenuItem.displayName = "MenuItem"; function MenuSubTrigger({ @@ -173,6 +175,7 @@ function MenuSeparator({ function Menu(props: MenuProps) { return ; } + const MenuSub = RadixMenu.Sub; const MenuGroup = RadixMenu.Group; const MenuGroupName = StyledMenuGroupname; diff --git a/app/client/packages/design-system/ads/src/Modal/Modal.stories.tsx b/app/client/packages/design-system/ads/src/Modal/Modal.stories.tsx index 7646acc9e6e..2892934ead0 100644 --- a/app/client/packages/design-system/ads/src/Modal/Modal.stories.tsx +++ b/app/client/packages/design-system/ads/src/Modal/Modal.stories.tsx @@ -63,6 +63,7 @@ export default { const ModalHeaderTemplate = (args: ModalHeaderProps) => { const [{}, updateArgs] = useArgs(); const changeOpenState = (state: boolean) => updateArgs({ open: state }); + return ( @@ -85,6 +86,7 @@ ModalHeaderStory.args = { const ModalBodyTemplate = (args: { children: React.ReactNode }) => { const [{}, updateArgs] = useArgs(); const changeOpenState = (state: boolean) => updateArgs({ open: state }); + return ( diff --git a/app/client/packages/design-system/ads/src/Modal/Modal.tsx b/app/client/packages/design-system/ads/src/Modal/Modal.tsx index 3447dd6eba8..ead5da93d7e 100644 --- a/app/client/packages/design-system/ads/src/Modal/Modal.tsx +++ b/app/client/packages/design-system/ads/src/Modal/Modal.tsx @@ -22,6 +22,7 @@ import { Button } from "../Button"; function ModalContent(props: ModalContentProps) { const { children, className, overlayClassName, ...rest } = props; + return ( @@ -75,6 +76,7 @@ function ModalTrigger(props: DialogTriggerProps) { const Modal = Root; const ModalBody = StyledBody; const ModalFooter = StyledFooter; + Modal.displayName = "Modal"; ModalContent.displayName = "ModalContent"; ModalHeader.displayName = "ModalHeader"; diff --git a/app/client/packages/design-system/ads/src/NumberInput/NumberInput.tsx b/app/client/packages/design-system/ads/src/NumberInput/NumberInput.tsx index a27c0598800..67ed676b9d0 100644 --- a/app/client/packages/design-system/ads/src/NumberInput/NumberInput.tsx +++ b/app/client/packages/design-system/ads/src/NumberInput/NumberInput.tsx @@ -52,6 +52,7 @@ const NumberInput = forwardRef( useEffect(() => { if (inputRef.current) { inputRef.current.addEventListener("keydown", handleKeyDown); + return () => { inputRef.current?.removeEventListener("keydown", handleKeyDown); }; @@ -61,6 +62,7 @@ const NumberInput = forwardRef( useEffect(() => { if (props.value !== undefined) { const newValue = handlePrefixAndSuffix(props.value); + setValue(newValue); } }, [props.value]); @@ -99,15 +101,20 @@ const NumberInput = forwardRef( const handlePrefixAndSuffix = (value: string) => { let newValue = value; + if (newValue === "" || newValue === undefined) return ""; + // defensive check to make sure the value is a string newValue = newValue.toString(); + if (prefix && !newValue.startsWith(prefix)) { newValue = prefix + newValue; } + if (suffix && !newValue.endsWith(suffix)) { newValue = newValue + suffix; } + return newValue; }; @@ -117,27 +124,33 @@ const NumberInput = forwardRef( // Check if the input value is a valid number if (!isNaN(inputValue)) { let newValue = inputValue; + // Apply operation on the value if (operation === "add") { newValue += scale; } else if (operation === "subtract") { newValue -= scale; } + // Check min and max values if (typeof min === "number" && newValue < min) { newValue = min; } + if (typeof max === "number" && newValue > max) { newValue = max; } + // Convert the value back to a string and append prefix and postfix if present let newValueString = String(newValue); + newValueString = handlePrefixAndSuffix(newValueString); setValue(newValueString); onChange?.(newValueString); } else { setValue(""); onChange?.(""); + return; } }; @@ -146,6 +159,7 @@ const NumberInput = forwardRef( if (typeof min === "number") { return getNumericalValue(value) <= min; } + return false; }; @@ -153,6 +167,7 @@ const NumberInput = forwardRef( if (typeof max === "number") { return getNumericalValue(value) >= max; } + return false; }; diff --git a/app/client/packages/design-system/ads/src/Popover/Popover.tsx b/app/client/packages/design-system/ads/src/Popover/Popover.tsx index 17d3b16ed71..34fc8d7ef27 100644 --- a/app/client/packages/design-system/ads/src/Popover/Popover.tsx +++ b/app/client/packages/design-system/ads/src/Popover/Popover.tsx @@ -50,6 +50,7 @@ function PopoverHeader({ ); } + function PopoverContent({ size = "sm", ...props }: PopoverContentProps) { return ( @@ -66,10 +67,12 @@ function PopoverContent({ size = "sm", ...props }: PopoverContentProps) { } const Popover = Root; + Popover.displayName = "Popover"; Popover.defaultProps = {}; const PopoverBody = StyledBody; + PopoverBody.displayName = "Popover Body"; export { Popover, PopoverBody, PopoverContent, PopoverHeader, PopoverTrigger }; diff --git a/app/client/packages/design-system/ads/src/Radio/RadioGroup.stories.tsx b/app/client/packages/design-system/ads/src/Radio/RadioGroup.stories.tsx index 9c0aad9188a..4d9eef6d336 100644 --- a/app/client/packages/design-system/ads/src/Radio/RadioGroup.stories.tsx +++ b/app/client/packages/design-system/ads/src/Radio/RadioGroup.stories.tsx @@ -56,6 +56,7 @@ export function RadioTabStory() { }, ]; const [selectedValue, setSelectedValue] = useState(possibleValues[0]); + return ( ; } + return ; } @@ -71,6 +72,7 @@ function Select(props: SelectProps) { showSearch={showSearch} tagRender={(props) => { const { closable, label, onClose } = props; + return ( {label} diff --git a/app/client/packages/design-system/ads/src/Tab/Tab.tsx b/app/client/packages/design-system/ads/src/Tab/Tab.tsx index 70fabdf4b28..c71c132cbb3 100644 --- a/app/client/packages/design-system/ads/src/Tab/Tab.tsx +++ b/app/client/packages/design-system/ads/src/Tab/Tab.tsx @@ -32,6 +32,7 @@ import { function Tabs(props: TabsProps) { const { className, defaultValue, ...rest } = props; + return ( ); } + export { Tabs, TabsList, Tab, TabPanel }; diff --git a/app/client/packages/design-system/ads/src/Table/Table.tsx b/app/client/packages/design-system/ads/src/Table/Table.tsx index 32fd3d33c11..4f2a4c6b673 100644 --- a/app/client/packages/design-system/ads/src/Table/Table.tsx +++ b/app/client/packages/design-system/ads/src/Table/Table.tsx @@ -39,6 +39,7 @@ function Table({ cell: StyledCell, }, }; + return ( {...props} diff --git a/app/client/packages/design-system/ads/src/Toast/Toast.tsx b/app/client/packages/design-system/ads/src/Toast/Toast.tsx index 6f96522219d..26326aa9a4b 100644 --- a/app/client/packages/design-system/ads/src/Toast/Toast.tsx +++ b/app/client/packages/design-system/ads/src/Toast/Toast.tsx @@ -37,6 +37,7 @@ const toast = { const icon = getIconByKind(options?.kind); // generate a unique toastId with the options given to it const toastId = JSON.stringify({ ...options, content }); + return toastifyToast( {content} diff --git a/app/client/packages/design-system/ads/src/ToggleButtonGroup/ToggleButtonGroup.tsx b/app/client/packages/design-system/ads/src/ToggleButtonGroup/ToggleButtonGroup.tsx index ac07c489728..79b49e44f1b 100644 --- a/app/client/packages/design-system/ads/src/ToggleButtonGroup/ToggleButtonGroup.tsx +++ b/app/client/packages/design-system/ads/src/ToggleButtonGroup/ToggleButtonGroup.tsx @@ -41,6 +41,7 @@ export const ToggleButtonGroup = React.forwardRef< case "ArrowRight": case "Right": const rightIndex = index === options.length - 1 ? 0 : index + 1; + toggleRefs[rightIndex]?.focus(); setFocusedIndex(rightIndex); break; @@ -48,6 +49,7 @@ export const ToggleButtonGroup = React.forwardRef< case "ArrowLeft": case "Left": const leftIndex = index === 0 ? options.length - 1 : index - 1; + toggleRefs[leftIndex]?.focus(); setFocusedIndex(leftIndex); break; @@ -72,6 +74,7 @@ export const ToggleButtonGroup = React.forwardRef< > {options.map(({ icon, value }: ToggleGroupOption, index: number) => { const isSelected = valueSet.has(value); + return (
{ if (triggerRef?.current != null) { setAriaAttrs(triggerRef?.current, referenceProps); diff --git a/app/client/packages/design-system/headless/src/components/Popover/stories/ControlledPopover.tsx b/app/client/packages/design-system/headless/src/components/Popover/stories/ControlledPopover.tsx index 266a6772a84..51baf3b4344 100644 --- a/app/client/packages/design-system/headless/src/components/Popover/stories/ControlledPopover.tsx +++ b/app/client/packages/design-system/headless/src/components/Popover/stories/ControlledPopover.tsx @@ -8,6 +8,7 @@ import { Button } from "react-aria-components"; export const ControlledPopover = () => { const [isOpen, setIsOpen] = useState(false); + return ( diff --git a/app/client/packages/design-system/headless/src/components/TextArea/src/TextArea.tsx b/app/client/packages/design-system/headless/src/components/TextArea/src/TextArea.tsx index d787d6e408e..691d8d8c7b8 100644 --- a/app/client/packages/design-system/headless/src/components/TextArea/src/TextArea.tsx +++ b/app/client/packages/design-system/headless/src/components/TextArea/src/TextArea.tsx @@ -43,15 +43,18 @@ function TextArea(props: TextAreaProps, ref: TextAreaRef) { // The measure/applied height is also incorrect/reset if we turn on and off // overflow: hidden in Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1787062 const isFirefox = "MozAppearance" in input.style; + if (!isFirefox) { input.style.overflow = "hidden"; } + input.style.alignSelf = "start"; input.style.height = "auto"; const computedStyle = getComputedStyle(input); const paddingTop = parseFloat(computedStyle.paddingTop); const paddingBottom = parseFloat(computedStyle.paddingBottom); + input.style.height = `${ // subtract comptued padding and border to get the actual content height input.scrollHeight - @@ -113,4 +116,5 @@ function TextArea(props: TextAreaProps, ref: TextAreaRef) { * are available to text fields. */ const _TextArea = React.forwardRef(TextArea); + export { _TextArea as TextArea }; diff --git a/app/client/packages/design-system/headless/src/components/TextInput/src/TextInput.tsx b/app/client/packages/design-system/headless/src/components/TextInput/src/TextInput.tsx index 9acebf15b34..12687d1c282 100644 --- a/app/client/packages/design-system/headless/src/components/TextInput/src/TextInput.tsx +++ b/app/client/packages/design-system/headless/src/components/TextInput/src/TextInput.tsx @@ -51,4 +51,5 @@ function TextInput(props: TextInputProps, ref: TextInputRef) { } const _TextInput = forwardRef(TextInput); + export { _TextInput as TextInput }; diff --git a/app/client/packages/design-system/headless/src/components/TextInputBase/src/TextInputBase.tsx b/app/client/packages/design-system/headless/src/components/TextInputBase/src/TextInputBase.tsx index 4a259ec2d96..25d0f401627 100644 --- a/app/client/packages/design-system/headless/src/components/TextInputBase/src/TextInputBase.tsx +++ b/app/client/packages/design-system/headless/src/components/TextInputBase/src/TextInputBase.tsx @@ -101,4 +101,5 @@ function TextInputBase(props: TextInputBaseProps, ref: Ref) { } const _TextInputBase = forwardRef(TextInputBase); + export { _TextInputBase as TextInputBase }; diff --git a/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts b/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts index 9aab75702fd..f4459cc1cdd 100644 --- a/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts +++ b/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts @@ -31,6 +31,7 @@ export class DarkModeTheme implements ColorModeTheme { isYellow, lightness, } = new ColorsAccessor(color); + this.seedColor = seedColor; this.seedLightness = lightness; this.seedChroma = chroma; @@ -154,6 +155,7 @@ export class DarkModeTheme implements ColorModeTheme { if (!this.seedIsAchromatic && !this.seedIsCold) { color.oklch.c = 0.012; } + return color; } @@ -441,6 +443,7 @@ export class DarkModeTheme implements ColorModeTheme { if (this.seedHue < 145) { color.oklch.h = 155; } + if (this.seedHue >= 145) { color.oklch.h = 135; } @@ -501,6 +504,7 @@ export class DarkModeTheme implements ColorModeTheme { if (this.seedHue < 27) { color.oklch.h = 32; } + if (this.seedHue >= 27) { color.oklch.h = 22; } @@ -561,6 +565,7 @@ export class DarkModeTheme implements ColorModeTheme { if (this.seedHue < 85) { color.oklch.h = 95; } + if (this.seedHue >= 85) { color.oklch.h = 70; } @@ -760,6 +765,7 @@ export class DarkModeTheme implements ColorModeTheme { private get fgNegative() { // Negative foreground is produced from the initially adjusted background color (see above). Additional tweaks are applied to make sure it's distinct from fgAccent when seed is red. const color = this.bgNegative.clone(); + color.oklch.l += 0.05; color.oklch.c += 0.1; color.oklch.h -= 10; @@ -938,6 +944,7 @@ export class DarkModeTheme implements ColorModeTheme { if (this.seedIsAchromatic) { color.oklch.c = 0; } + // For light content on dark background APCA contrast is negative. −15 is “The absolute minimum for any non-text that needs to be discernible and differentiable, but does not apply to semantic non-text such as icons”. if (this.bg.contrastAPCA(this.seedColor) >= -15) { color.oklch.l += 0.05; diff --git a/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts b/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts index 9cb0499528a..caef0659aa2 100644 --- a/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts +++ b/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts @@ -29,6 +29,7 @@ export class LightModeTheme implements ColorModeTheme { isYellow, lightness, } = new ColorsAccessor(color); + this.seedColor = seedColor; this.seedLightness = lightness; this.seedChroma = chroma; @@ -434,6 +435,7 @@ export class LightModeTheme implements ColorModeTheme { if (this.seedHue < 145) { color.oklch.h = 155; } + if (this.seedHue >= 145) { color.oklch.h = 135; } @@ -494,6 +496,7 @@ export class LightModeTheme implements ColorModeTheme { if (this.seedHue < 27) { color.oklch.h = 34; } + if (this.seedHue >= 27) { color.oklch.h = 20; } @@ -554,6 +557,7 @@ export class LightModeTheme implements ColorModeTheme { if (this.seedHue < 85) { color.oklch.h = 95; } + if (this.seedHue >= 85) { color.oklch.h = 70; } @@ -633,6 +637,7 @@ export class LightModeTheme implements ColorModeTheme { if (!this.seedIsVeryLight) { color.oklch.l += 0.015; } + return color; } diff --git a/app/client/packages/design-system/theming/src/color/tests/DarkModeTheme.test.ts b/app/client/packages/design-system/theming/src/color/tests/DarkModeTheme.test.ts index b20846aedcb..3e99872c824 100644 --- a/app/client/packages/design-system/theming/src/color/tests/DarkModeTheme.test.ts +++ b/app/client/packages/design-system/theming/src/color/tests/DarkModeTheme.test.ts @@ -3,11 +3,13 @@ import { DarkModeTheme } from "../src/DarkModeTheme"; describe("bg color", () => { it("should return correct color when chroma < 0.04", () => { const { bg } = new DarkModeTheme("oklch(0.92 0.02 110)").getColors(); + expect(bg).toBe("rgb(4.3484% 4.3484% 4.3484%)"); }); it("should return correct color when chroma > 0.04", () => { const { bg } = new DarkModeTheme("oklch(0.92 0.05 110)").getColors(); + expect(bg).toBe("rgb(4.4523% 4.5607% 2.4575%)"); }); }); @@ -15,6 +17,7 @@ describe("bg color", () => { describe("bgAccent color", () => { it("should return correct color when lightness < 0.3", () => { const { bgAccent } = new DarkModeTheme("oklch(0.2 0.09 231)").getColors(); + expect(bgAccent).toBe("rgb(0% 20.243% 31.25%)"); }); }); @@ -24,6 +27,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new DarkModeTheme( "oklch(0.2 0.09 231)", ).getColors(); + expect(bgAccentHover).toBe("rgb(0% 25.612% 37.776%)"); }); @@ -31,6 +35,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new DarkModeTheme( "oklch(0.35 0.09 231)", ).getColors(); + expect(bgAccentHover).toBe("rgb(0% 29.954% 42.35%)"); }); @@ -38,6 +43,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new DarkModeTheme( "oklch(0.50 0.09 231)", ).getColors(); + expect(bgAccentHover).toBe("rgb(15.696% 45.773% 58.926%)"); }); @@ -45,6 +51,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new DarkModeTheme( "oklch(0.80 0.09 150)", ).getColors(); + expect(bgAccentHover).toBe("rgb(51.184% 89.442% 60.062%)"); }); @@ -52,6 +59,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new DarkModeTheme( "oklch(0.80 0.09 110)", ).getColors(); + expect(bgAccentHover).toBe("rgb(85.364% 85.594% 0%)"); }); @@ -59,6 +67,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new DarkModeTheme( "oklch(0.80 0.03 110)", ).getColors(); + expect(bgAccentHover).toBe("rgb(79.687% 80.239% 71.58%)"); }); @@ -66,6 +75,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new DarkModeTheme( "oklch(0.90 0.03 110)", ).getColors(); + expect(bgAccentHover).toBe("rgb(78.426% 78.975% 70.34%)"); }); }); @@ -75,6 +85,7 @@ describe("bgAccentActive color", () => { const { bgAccentActive } = new DarkModeTheme( "oklch(0.2 0.09 231)", ).getColors(); + expect(bgAccentActive).toBe("rgb(0% 18.133% 28.462%)"); }); @@ -82,6 +93,7 @@ describe("bgAccentActive color", () => { const { bgAccentActive } = new DarkModeTheme( "oklch(0.45 0.09 231)", ).getColors(); + expect(bgAccentActive).toBe("rgb(0% 32.155% 44.665%)"); }); @@ -89,6 +101,7 @@ describe("bgAccentActive color", () => { const { bgAccentActive } = new DarkModeTheme( "oklch(0.75 0.09 231)", ).getColors(); + expect(bgAccentActive).toBe("rgb(37.393% 66.165% 80.119%)"); }); @@ -96,6 +109,7 @@ describe("bgAccentActive color", () => { const { bgAccentActive } = new DarkModeTheme( "oklch(0.90 0.09 231)", ).getColors(); + expect(bgAccentActive).toBe("rgb(46.054% 74.898% 89.15%)"); }); }); @@ -105,6 +119,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new DarkModeTheme( "oklch(0.30 0.09 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(0% 15.035% 24.345%)"); }); @@ -112,6 +127,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new DarkModeTheme( "oklch(0.15 0.09 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(0% 10.07% 17.756%)"); }); @@ -119,6 +135,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new DarkModeTheme( "oklch(0.30 0.15 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(0% 15.035% 24.348%)"); }); @@ -126,6 +143,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new DarkModeTheme( "oklch(0.30 0.03 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(13.15% 13.15% 13.15%)"); }); }); @@ -135,6 +153,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new DarkModeTheme( "oklch(0.30 0.09 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(0% 15.035% 24.345%)"); }); @@ -142,6 +161,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new DarkModeTheme( "oklch(0.15 0.09 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(0% 10.07% 17.756%)"); }); @@ -149,6 +169,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new DarkModeTheme( "oklch(0.30 0.15 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(0% 15.035% 24.348%)"); }); @@ -156,6 +177,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new DarkModeTheme( "oklch(0.30 0.03 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(13.15% 13.15% 13.15%)"); }); }); @@ -165,6 +187,7 @@ describe("bgAccentSubtleHover color", () => { const { bgAccentSubtleHover } = new DarkModeTheme( "oklch(0.35 0.09 70)", ).getColors(); + expect(bgAccentSubtleHover).toBe("rgb(25.471% 12.268% 0%)"); }); }); @@ -174,6 +197,7 @@ describe("bgAccentSubtleActive color", () => { const { bgAccentSubtleActive } = new DarkModeTheme( "oklch(0.35 0.09 70)", ).getColors(); + expect(bgAccentSubtleActive).toBe("rgb(19.068% 8.15% 0%)"); }); }); @@ -183,6 +207,7 @@ describe("bgAssistive color", () => { const { bgAssistive } = new DarkModeTheme( "oklch(0.95 0.03 170)", ).getColors(); + expect(bgAssistive).toBe("rgb(92.148% 92.148% 92.148%)"); }); }); @@ -190,21 +215,25 @@ describe("bgAssistive color", () => { describe("bgNeutral color", () => { it("should return correct color when lightness < 0.5", () => { const { bgNeutral } = new DarkModeTheme("oklch(0.3 0.09 231)").getColors(); + expect(bgNeutral).toEqual("rgb(14.004% 18.746% 21.224%)"); }); it("should return correct color when chroma < 0.04", () => { const { bgNeutral } = new DarkModeTheme("oklch(0.95 0.02 170)").getColors(); + expect(bgNeutral).toEqual("rgb(23.919% 23.919% 23.919%)"); }); it("should return correct color when hue is between 120 and 300 and chroma is not less than 0.04", () => { const { bgNeutral } = new DarkModeTheme("oklch(0.95 0.06 240)").getColors(); + expect(bgNeutral).toEqual("rgb(20.329% 24.6% 27.739%)"); }); it("should return correct color when hue is not between 120 and 300 and chroma is not less than 0.04", () => { const { bgNeutral } = new DarkModeTheme("oklch(0.95 0.06 30)").getColors(); + expect(bgNeutral).toEqual("rgb(25.969% 23.236% 22.736%)"); }); }); @@ -214,6 +243,7 @@ describe("bgNeutralOpacity color", () => { const { bgNeutralOpacity } = new DarkModeTheme( "oklch(0.51 0.24 279)", ).getColors(); + expect(bgNeutralOpacity).toEqual("rgb(1.7871% 1.9891% 5.049% / 0.7)"); }); }); @@ -223,6 +253,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new DarkModeTheme( "oklch(0.86 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(25.976% 25.976% 25.976%)"); }); @@ -230,6 +261,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new DarkModeTheme( "oklch(0.80 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(24.944% 24.944% 24.944%)"); }); @@ -237,6 +269,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new DarkModeTheme( "oklch(0.60 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(32.307% 32.307% 32.307%)"); }); @@ -244,6 +277,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new DarkModeTheme( "oklch(0.35 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(27.015% 27.015% 27.015%)"); }); }); @@ -253,6 +287,7 @@ describe("bgNeutralActive color", () => { const { bgNeutralActive } = new DarkModeTheme( "oklch(0.39 0.03 170)", ).getColors(); + expect(bgNeutralActive).toEqual("rgb(25.976% 25.976% 25.976%)"); }); @@ -260,6 +295,7 @@ describe("bgNeutralActive color", () => { const { bgNeutralActive } = new DarkModeTheme( "oklch(0.6 0.03 170)", ).getColors(); + expect(bgNeutralActive).toEqual("rgb(27.015% 27.015% 27.015%)"); }); @@ -267,6 +303,7 @@ describe("bgNeutralActive color", () => { const { bgNeutralActive } = new DarkModeTheme( "oklch(0.8 0.03 170)", ).getColors(); + expect(bgNeutralActive).toEqual("rgb(19.892% 19.892% 19.892%)"); }); @@ -274,6 +311,7 @@ describe("bgNeutralActive color", () => { const { bgNeutralActive } = new DarkModeTheme( "oklch(0.9 0.03 170)", ).getColors(); + expect(bgNeutralActive).toEqual("rgb(24.944% 24.944% 24.944%)"); }); }); @@ -283,6 +321,7 @@ describe("bgNeutralSubtle color", () => { const { bgNeutralSubtle } = new DarkModeTheme( "oklch(0.3 0.03 170)", ).getColors(); + expect(bgNeutralSubtle).toEqual("rgb(16.952% 16.952% 16.952%)"); }); @@ -290,6 +329,7 @@ describe("bgNeutralSubtle color", () => { const { bgNeutralSubtle } = new DarkModeTheme( "oklch(0.15 0.03 170)", ).getColors(); + expect(bgNeutralSubtle).toEqual("rgb(10.396% 10.396% 10.396%)"); }); @@ -297,6 +337,7 @@ describe("bgNeutralSubtle color", () => { const { bgNeutralSubtle } = new DarkModeTheme( "oklch(0.3 0.03 170)", ).getColors(); + expect(bgNeutralSubtle).toEqual("rgb(16.952% 16.952% 16.952%)"); }); @@ -304,6 +345,7 @@ describe("bgNeutralSubtle color", () => { const { bgNeutralSubtle } = new DarkModeTheme( "oklch(0.3 0.01 170)", ).getColors(); + expect(bgNeutralSubtle).toEqual("rgb(16.952% 16.952% 16.952%)"); }); }); @@ -313,6 +355,7 @@ describe("bgNeutralSubtleHover color", () => { const { bgNeutralSubtleHover } = new DarkModeTheme( "oklch(0.3 0.01 170)", ).getColors(); + expect(bgNeutralSubtleHover).toEqual("rgb(19.892% 19.892% 19.892%)"); }); }); @@ -322,6 +365,7 @@ describe("bgNeutralSubtleActive color", () => { const { bgNeutralSubtleActive } = new DarkModeTheme( "oklch(0.3 0.01 170)", ).getColors(); + expect(bgNeutralSubtleActive).toEqual("rgb(15.033% 15.033% 15.033%)"); }); }); @@ -331,6 +375,7 @@ describe("bgPositive color", () => { const { bgPositive } = new DarkModeTheme( "oklch(0.62 0.17 145)", ).getColors(); + expect(bgPositive).toEqual("rgb(33.244% 60.873% 10.585%)"); }); @@ -338,6 +383,7 @@ describe("bgPositive color", () => { const { bgPositive } = new DarkModeTheme( "oklch(0.62 0.08 145)", ).getColors(); + expect(bgPositive).toEqual("rgb(18.515% 62.493% 23.87%)"); }); @@ -345,6 +391,7 @@ describe("bgPositive color", () => { const { bgPositive } = new DarkModeTheme( "oklch(0.62 0.17 100)", ).getColors(); + expect(bgPositive).toEqual("rgb(18.515% 62.493% 23.87%)"); }); @@ -352,6 +399,7 @@ describe("bgPositive color", () => { const { bgPositive } = new DarkModeTheme( "oklch(0.62 0.08 100)", ).getColors(); + expect(bgPositive).toEqual("rgb(18.515% 62.493% 23.87%)"); }); }); @@ -361,6 +409,7 @@ describe("bgPositiveHover color", () => { const { bgPositiveHover } = new DarkModeTheme( "oklch(0.62 0.17 145)", ).getColors(); + expect(bgPositiveHover).toEqual("rgb(36.781% 64.586% 15.952%)"); }); }); @@ -370,6 +419,7 @@ describe("bgPositiveActive color", () => { const { bgPositiveActive } = new DarkModeTheme( "oklch(0.62 0.17 145)", ).getColors(); + expect(bgPositiveActive).toEqual("rgb(28.549% 55.976% 0%)"); }); }); @@ -379,6 +429,7 @@ describe("bgPositiveSubtle color", () => { const { bgPositiveSubtle } = new DarkModeTheme( "oklch(0.62 0.17 145)", ).getColors(); + expect(bgPositiveSubtle).toEqual("rgb(7.8895% 15.556% 2.8063%)"); }); }); @@ -388,6 +439,7 @@ describe("bgPositiveSubtleHover color", () => { const { bgPositiveSubtleHover } = new DarkModeTheme( "oklch(0.62 0.17 145)", ).getColors(); + expect(bgPositiveSubtleHover).toEqual("rgb(10.689% 18.514% 5.7924%)"); }); }); @@ -397,6 +449,7 @@ describe("bgPositiveSubtleActive color", () => { const { bgPositiveSubtleActive } = new DarkModeTheme( "oklch(0.62 0.17 145)", ).getColors(); + expect(bgPositiveSubtleActive).toEqual("rgb(6.0555% 13.622% 1.2799%)"); }); }); @@ -404,21 +457,25 @@ describe("bgPositiveSubtleActive color", () => { describe("bgNegative color", () => { it("should return correct color when seed color is red (hue between 5 and 49) and chroma > 0.07", () => { const { bgNegative } = new DarkModeTheme("oklch(0.55 0.22 27)").getColors(); + expect(bgNegative).toEqual("rgb(83.034% 1.7746% 18.798%)"); }); it("should return correct color when seed color is red (hue between 5 and 49) but chroma is not greater than 0.07", () => { const { bgNegative } = new DarkModeTheme("oklch(0.55 0.05 27)").getColors(); + expect(bgNegative).toEqual("rgb(83.108% 4.6651% 10.252%)"); }); it("should return correct color when seed color is not red (hue outside 5-49) and chroma > 0.07", () => { const { bgNegative } = new DarkModeTheme("oklch(0.55 0.22 60)").getColors(); + expect(bgNegative).toEqual("rgb(83.108% 4.6651% 10.252%)"); }); it("should return correct color when seed color is not red (hue outside 5-49) and chroma is not greater than 0.07", () => { const { bgNegative } = new DarkModeTheme("oklch(0.55 0.05 60)").getColors(); + expect(bgNegative).toEqual("rgb(83.108% 4.6651% 10.252%)"); }); }); @@ -428,6 +485,7 @@ describe("bgNegativeHover color", () => { const { bgNegativeHover } = new DarkModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeHover).toEqual("rgb(87.347% 11.876% 22.315%)"); }); }); @@ -437,6 +495,7 @@ describe("bgNegativeActive color", () => { const { bgNegativeActive } = new DarkModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeActive).toEqual("rgb(77.305% 0% 13.994%)"); }); }); @@ -446,6 +505,7 @@ describe("bgNegativeSubtle color", () => { const { bgNegativeSubtle } = new DarkModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeSubtle).toEqual("rgb(77.305% 0% 13.994%)"); }); }); @@ -455,6 +515,7 @@ describe("bgNegativeSubtleHover color", () => { const { bgNegativeSubtleHover } = new DarkModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeSubtleHover).toEqual("rgb(29.827% 6.1224% 7.5796%)"); }); }); @@ -464,6 +525,7 @@ describe("bgNegativeSubtleActive color", () => { const { bgNegativeSubtleActive } = new DarkModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeSubtleActive).toEqual("rgb(24.04% 0.52339% 2.9937%)"); }); }); @@ -471,21 +533,25 @@ describe("bgNegativeSubtleActive color", () => { describe("bgWarning color", () => { it("should return correct color when seed color is yellow (hue between 60 and 115) and chroma > 0.09", () => { const { bgWarning } = new DarkModeTheme("oklch(0.75 0.15 85)").getColors(); + expect(bgWarning).toEqual("rgb(91.527% 60.669% 16.491%)"); }); it("should return correct color when seed color is yellow (hue between 60 and 115) but chroma is not greater than 0.09", () => { const { bgWarning } = new DarkModeTheme("oklch(0.75 0.05 85)").getColors(); + expect(bgWarning).toEqual("rgb(85.145% 64.66% 8.0285%)"); }); it("should return correct color when seed color is not yellow (hue outside 60-115) and chroma > 0.09", () => { const { bgWarning } = new DarkModeTheme("oklch(0.75 0.15 50)").getColors(); + expect(bgWarning).toEqual("rgb(85.145% 64.66% 8.0285%)"); }); it("should return correct color when seed color is not yellow (hue outside 60-115) and chroma is not greater than 0.09", () => { const { bgWarning } = new DarkModeTheme("oklch(0.75 0.05 50)").getColors(); + expect(bgWarning).toEqual("rgb(85.145% 64.66% 8.0285%)"); }); }); @@ -495,6 +561,7 @@ describe("bgWarningHover color", () => { const { bgWarningHover } = new DarkModeTheme( "oklch(0.75 0.05 50)", ).getColors(); + expect(bgWarningHover).toEqual("rgb(90.341% 69.671% 17.643%)"); }); }); @@ -504,6 +571,7 @@ describe("bgWarningActive color", () => { const { bgWarningActive } = new DarkModeTheme( "oklch(0.75 0.05 50)", ).getColors(); + expect(bgWarningActive).toEqual("rgb(78.726% 58.477% 0%)"); }); }); @@ -513,6 +581,7 @@ describe("bgWarningSubtle color", () => { const { bgWarningSubtle } = new DarkModeTheme( "oklch(0.75 0.05 50)", ).getColors(); + expect(bgWarningSubtle).toEqual("rgb(16.622% 12.537% 3.3792%)"); }); }); @@ -522,6 +591,7 @@ describe("bgWarningSubtleHover color", () => { const { bgWarningSubtleHover } = new DarkModeTheme( "oklch(0.75 0.05 50)", ).getColors(); + expect(bgWarningSubtleHover).toEqual("rgb(19.571% 15.398% 6.3225%)"); }); }); @@ -531,6 +601,7 @@ describe("bgWarningSubtleActive color", () => { const { bgWarningSubtleActive } = new DarkModeTheme( "oklch(0.75 0.05 50)", ).getColors(); + expect(bgWarningSubtleActive).toEqual("rgb(14.696% 10.673% 1.7722%)"); }); }); @@ -720,6 +791,7 @@ describe("fgOnWarning color ", () => { describe("bd color", () => { it("should return correct color", () => { const { bd } = new DarkModeTheme("oklch(0.45 0.5 60)").getColors(); + expect(bd).toEqual("rgb(30.094% 27.562% 25.617%)"); }); }); @@ -727,11 +799,13 @@ describe("bd color", () => { describe("bdAccent color", () => { it("should return correct color when chroma < 0.04", () => { const { bdAccent } = new DarkModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdAccent).toEqual("rgb(25.976% 25.976% 25.976%)"); }); it("should return correct color when chroma > 0.04", () => { const { bdAccent } = new DarkModeTheme("oklch(0.45 0.1 60)").getColors(); + expect(bdAccent).toEqual("rgb(33.765% 23.5% 15.034%)"); }); }); @@ -739,26 +813,31 @@ describe("bdAccent color", () => { describe("bdFocus color", () => { it("should return correct color when lightness < 0.4", () => { const { bdFocus } = new DarkModeTheme("oklch(0.3 0.4 60)").getColors(); + expect(bdFocus).toEqual("rgb(84.145% 71.694% 61.962%)"); }); it("should return correct color when lightness > 0.65", () => { const { bdFocus } = new DarkModeTheme("oklch(0.85 0.03 60)").getColors(); + expect(bdFocus).toEqual("rgb(96.595% 66.918% 41.877%)"); }); it("should return correct color when chroma < 0.12", () => { const { bdFocus } = new DarkModeTheme("oklch(0.85 0.1 60)").getColors(); + expect(bdFocus).toEqual("rgb(96.595% 66.918% 41.877%)"); }); it("should return correct color when hue is between 0 and 55", () => { const { bdFocus } = new DarkModeTheme("oklch(0.85 0.1 30)").getColors(); + expect(bdFocus).toEqual("rgb(100% 62.553% 56.236%)"); }); it("should return correct color when hue > 340", () => { const { bdFocus } = new DarkModeTheme("oklch(0.85 0.1 350)").getColors(); + expect(bdFocus).toEqual("rgb(97.244% 61.583% 78.647%)"); }); }); @@ -766,6 +845,7 @@ describe("bdFocus color", () => { describe("bdNeutral color", () => { it("should return correct color when chroma < 0.04", () => { const { bdNeutral } = new DarkModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdNeutral).toEqual("rgb(33.384% 33.384% 33.384%)"); }); }); @@ -775,6 +855,7 @@ describe("bdNeutralHover", () => { const { bdNeutralHover } = new DarkModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdNeutralHover).toEqual("rgb(50.211% 50.211% 50.211%)"); }); }); @@ -782,6 +863,7 @@ describe("bdNeutralHover", () => { describe("bdPositive", () => { it("should return correct color", () => { const { bdPositive } = new DarkModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdPositive).toEqual("rgb(27.641% 37.516% 27.759%)"); }); }); @@ -791,6 +873,7 @@ describe("bdPositiveHover", () => { const { bdPositiveHover } = new DarkModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdPositiveHover).toEqual("rgb(40.836% 51.186% 40.879%)"); }); }); @@ -798,6 +881,7 @@ describe("bdPositiveHover", () => { describe("bdNegative", () => { it("should return correct color", () => { const { bdNegative } = new DarkModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdNegative).toEqual("rgb(52.977% 24.763% 22.178%)"); }); }); @@ -807,6 +891,7 @@ describe("bdNegativeHover", () => { const { bdNegativeHover } = new DarkModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdNegativeHover).toEqual("rgb(65.578% 36.03% 32.932%)"); }); }); @@ -814,6 +899,7 @@ describe("bdNegativeHover", () => { describe("bdWarning", () => { it("should return correct color", () => { const { bdWarning } = new DarkModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdWarning).toEqual("rgb(48.431% 33.879% 0%)"); }); }); @@ -823,6 +909,7 @@ describe("bdWarningHover", () => { const { bdWarningHover } = new DarkModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdWarningHover).toEqual("rgb(63.866% 45.645% 0%)"); }); }); @@ -830,6 +917,7 @@ describe("bdWarningHover", () => { describe("bdOnAccent", () => { it("should return correct color", () => { const { bdOnAccent } = new DarkModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdOnAccent).toEqual("rgb(8.8239% 3.8507% 0.79169%)"); }); }); @@ -839,6 +927,7 @@ describe("bdOnNeutral", () => { const { bdOnNeutral } = new DarkModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnNeutral).toEqual("rgb(5.1758% 5.1758% 5.1758%)"); }); }); @@ -848,6 +937,7 @@ describe("bdOnPositive", () => { const { bdOnPositive } = new DarkModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnPositive).toEqual("rgb(0% 38.221% 0%)"); }); }); @@ -857,6 +947,7 @@ describe("bdOnNegative", () => { const { bdOnNegative } = new DarkModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnNegative).toEqual("rgb(38.766% 0% 0%)"); }); }); @@ -866,6 +957,7 @@ describe("bdOnWarning", () => { const { bdOnWarning } = new DarkModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnWarning).toEqual("rgb(51.176% 35.973% 0%)"); }); }); diff --git a/app/client/packages/design-system/theming/src/color/tests/LightModeTheme.test.ts b/app/client/packages/design-system/theming/src/color/tests/LightModeTheme.test.ts index 252bca1d05c..6e9473c4605 100644 --- a/app/client/packages/design-system/theming/src/color/tests/LightModeTheme.test.ts +++ b/app/client/packages/design-system/theming/src/color/tests/LightModeTheme.test.ts @@ -3,26 +3,31 @@ import { LightModeTheme } from "../src/LightModeTheme"; describe("bg color", () => { it("should return correct color when lightness > 0.93", () => { const { bg } = new LightModeTheme("oklch(0.95 0.09 231)").getColors(); + expect(bg).toBe("rgb(95.576% 96.181% 96.511%)"); }); it("should return correct color when lightness < 0.93", () => { const { bg } = new LightModeTheme("oklch(0.92 0.09 231)").getColors(); + expect(bg).toBe("rgb(95.576% 96.181% 96.511%)"); }); it("should return correct color when hue > 120 && hue < 300", () => { const { bg } = new LightModeTheme("oklch(0.95 0.07 231)").getColors(); + expect(bg).toBe("rgb(95.576% 96.181% 96.511%)"); }); it("should return correct color when hue < 120 or hue > 300", () => { const { bg } = new LightModeTheme("oklch(0.92 0.07 110)").getColors(); + expect(bg).toBe("rgb(96.069% 96.092% 95.796%)"); }); it("should return correct color when chroma < 0.04", () => { const { bg } = new LightModeTheme("oklch(0.92 0.02 110)").getColors(); + expect(bg).toBe("rgb(96.059% 96.059% 96.059%)"); }); }); @@ -30,6 +35,7 @@ describe("bg color", () => { describe("bgAccent color", () => { it("should return correct color when lightness > 0.93", () => { const { bgAccent } = new LightModeTheme("oklch(0.95 0.09 231)").getColors(); + expect(bgAccent).toBe("rgb(39.906% 72.747% 88.539%)"); }); }); @@ -39,6 +45,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.05 0.09 231)", ).getColors(); + expect(bgAccentHover).toBe("rgb(0% 23.472% 35.518%)"); }); @@ -46,6 +53,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.08 0.09 231)", ).getColors(); + expect(bgAccentHover).toBe("rgb(0% 18.133% 28.462%)"); }); @@ -53,6 +61,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.17 0.09 231)", ).getColors(); + expect(bgAccentHover).toBe("rgb(0% 17.091% 27.078%)"); }); @@ -60,6 +69,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.17 0.09 110)", ).getColors(); + expect(bgAccentHover).toBe("rgb(19.253% 19.006% 0%)"); }); @@ -67,6 +77,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.3 0.09 110)", ).getColors(); + expect(bgAccentHover).toBe("rgb(28.395% 28.425% 0%)"); }); @@ -74,6 +85,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.5 0.09 110)", ).getColors(); + expect(bgAccentHover).toBe("rgb(45.795% 46.287% 19.839%)"); }); @@ -81,6 +93,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.9 0.09 110)", ).getColors(); + expect(bgAccentHover).toBe("rgb(92.14% 93.271% 65.642%)"); }); @@ -88,6 +101,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.95 0.09 70)", ).getColors(); + expect(bgAccentHover).toBe("rgb(88.091% 67.511% 43.309%)"); }); @@ -95,6 +109,7 @@ describe("bgAccentHover color", () => { const { bgAccentHover } = new LightModeTheme( "oklch(0.95 0.09 120)", ).getColors(); + expect(bgAccentHover).toBe("rgb(68.605% 75.736% 46.633%)"); }); }); @@ -104,6 +119,7 @@ describe("bgAccentActive color", () => { const { bgAccentActive } = new LightModeTheme( "oklch(0.35 0.09 70)", ).getColors(); + expect(bgAccentActive).toBe("rgb(29.443% 14.834% 0%)"); }); @@ -111,6 +127,7 @@ describe("bgAccentActive color", () => { const { bgAccentActive } = new LightModeTheme( "oklch(0.50 0.09 70)", ).getColors(); + expect(bgAccentActive).toBe("rgb(49.27% 32.745% 10.549%)"); }); @@ -118,6 +135,7 @@ describe("bgAccentActive color", () => { const { bgAccentActive } = new LightModeTheme( "oklch(0.75 0.09 70)", ).getColors(); + expect(bgAccentActive).toBe("rgb(81.395% 63.124% 41.808%)"); }); @@ -125,6 +143,7 @@ describe("bgAccentActive color", () => { const { bgAccentActive } = new LightModeTheme( "oklch(0.95 0.09 70)", ).getColors(); + expect(bgAccentActive).toBe("rgb(82.901% 62.578% 38.456%)"); }); }); @@ -134,6 +153,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new LightModeTheme( "oklch(0.95 0.09 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(80.68% 97.025% 100%)"); }); @@ -141,6 +161,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new LightModeTheme( "oklch(0.92 0.09 231)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(73.159% 94.494% 100%)"); }); @@ -148,6 +169,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new LightModeTheme( "oklch(0.95 0.10 120)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(90.964% 97.964% 71.119%)"); }); @@ -155,6 +177,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new LightModeTheme( "oklch(0.95 0.07 170)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(75.944% 100% 91.359%)"); }); @@ -162,6 +185,7 @@ describe("bgAccentSubtle color", () => { const { bgAccentSubtle } = new LightModeTheme( "oklch(0.95 0.03 170)", ).getColors(); + expect(bgAccentSubtle).toBe("rgb(94.099% 94.099% 94.099%)"); }); }); @@ -171,6 +195,7 @@ describe("bgAccentSubtleHover color", () => { const { bgAccentSubtleHover } = new LightModeTheme( "oklch(0.35 0.09 70)", ).getColors(); + expect(bgAccentSubtleHover).toBe("rgb(100% 91.101% 76.695%)"); }); }); @@ -180,6 +205,7 @@ describe("bgAccentSubtleActive color", () => { const { bgAccentSubtleActive } = new LightModeTheme( "oklch(0.35 0.09 70)", ).getColors(); + expect(bgAccentSubtleActive).toBe("rgb(100% 87.217% 72.911%)"); }); }); @@ -189,6 +215,7 @@ describe("bgAssistive color", () => { const { bgAssistive } = new LightModeTheme( "oklch(0.95 0.03 170)", ).getColors(); + expect(bgAssistive).toBe("rgb(5.1758% 5.1758% 5.1758%)"); }); }); @@ -198,11 +225,13 @@ describe("bgNeutral color", () => { const { bgNeutral } = new LightModeTheme( "oklch(0.95 0.03 170)", ).getColors(); + expect(bgNeutral).toEqual("rgb(0% 0% 0%)"); }); it("should return correct color when lightness is between 0.25 and 0.85", () => { const { bgNeutral } = new LightModeTheme("oklch(0.5 0.09 231)").getColors(); + expect(bgNeutral).toEqual("rgb(4.0418% 4.4239% 4.6309%)"); }); @@ -210,6 +239,7 @@ describe("bgNeutral color", () => { const { bgNeutral } = new LightModeTheme( "oklch(0.95 0.02 170)", ).getColors(); + expect(bgNeutral).toEqual("rgb(0% 0% 0%)"); }); @@ -217,11 +247,13 @@ describe("bgNeutral color", () => { const { bgNeutral } = new LightModeTheme( "oklch(0.95 0.06 240)", ).getColors(); + expect(bgNeutral).toEqual("rgb(0% 0% 0%)"); }); it("should return correct color when hue is not between 120 and 300 and chroma is not less than 0.04", () => { const { bgNeutral } = new LightModeTheme("oklch(0.95 0.06 30)").getColors(); + expect(bgNeutral).toEqual("rgb(0% 0% 0%)"); }); }); @@ -231,6 +263,7 @@ describe("bgNeutralOpacity color", () => { const { bgNeutralOpacity } = new LightModeTheme( "oklch(0.51 0.24 279)", ).getColors(); + expect(bgNeutralOpacity).toEqual("rgb(4.2704% 4.3279% 4.6942% / 0.6)"); }); }); @@ -240,6 +273,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new LightModeTheme( "oklch(0.05 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(22.901% 22.901% 22.901%)"); }); @@ -247,6 +281,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new LightModeTheme( "oklch(0.10 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(16.952% 16.952% 16.952%)"); }); @@ -254,6 +289,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new LightModeTheme( "oklch(0.17 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(15.988% 15.988% 15.988%)"); }); @@ -261,6 +297,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new LightModeTheme( "oklch(0.35 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(17.924% 17.924% 17.924%)"); }); @@ -268,6 +305,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new LightModeTheme( "oklch(0.75 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(4.3484% 4.3484% 4.3484%)"); }); @@ -275,6 +313,7 @@ describe("bgNeutralHover color", () => { const { bgNeutralHover } = new LightModeTheme( "oklch(0.96 0.03 170)", ).getColors(); + expect(bgNeutralHover).toEqual("rgb(4.3484% 4.3484% 4.3484%)"); }); }); @@ -284,6 +323,7 @@ describe("bgNeutralActive color", () => { const { bgNeutralActive } = new LightModeTheme( "oklch(0.35 0.03 170)", ).getColors(); + expect(bgNeutralActive).toEqual("rgb(0% 0% 0%)"); }); @@ -291,6 +331,7 @@ describe("bgNeutralActive color", () => { const { bgNeutralActive } = new LightModeTheme( "oklch(0.80 0.03 170)", ).getColors(); + expect(bgNeutralActive).toEqual("rgb(0% 0% 0%)"); }); @@ -298,6 +339,7 @@ describe("bgNeutralActive color", () => { const { bgNeutralActive } = new LightModeTheme( "oklch(0.96 0.03 170)", ).getColors(); + expect(bgNeutralActive).toEqual("rgb(0% 0% 0%)"); }); }); @@ -307,6 +349,7 @@ describe("bgNeutralSubtle color", () => { const { bgNeutralSubtle } = new LightModeTheme( "oklch(0.95 0.03 170)", ).getColors(); + expect(bgNeutralSubtle).toEqual("rgb(93.635% 94.291% 94.022%)"); }); @@ -314,6 +357,7 @@ describe("bgNeutralSubtle color", () => { const { bgNeutralSubtle } = new LightModeTheme( "oklch(0.92 0.03 170)", ).getColors(); + expect(bgNeutralSubtle).toEqual("rgb(95.592% 96.251% 95.981%)"); }); @@ -321,6 +365,7 @@ describe("bgNeutralSubtle color", () => { const { bgNeutralSubtle } = new LightModeTheme( "oklch(0.92 0.1 170)", ).getColors(); + expect(bgNeutralSubtle).toEqual("rgb(95.592% 96.251% 95.981%)"); }); @@ -328,6 +373,7 @@ describe("bgNeutralSubtle color", () => { const { bgNeutralSubtle } = new LightModeTheme( "oklch(0.92 0.03 170)", ).getColors(); + expect(bgNeutralSubtle).toEqual("rgb(95.592% 96.251% 95.981%)"); }); }); @@ -357,6 +403,7 @@ describe("bgPositive color", () => { const { bgPositive } = new LightModeTheme( "oklch(0.62 0.19 145)", ).getColors(); + expect(bgPositive).toEqual("rgb(30.224% 61.63% 0%)"); }); @@ -364,6 +411,7 @@ describe("bgPositive color", () => { const { bgPositive } = new LightModeTheme( "oklch(0.62 0.1 145)", ).getColors(); + expect(bgPositive).toEqual("rgb(6.7436% 63.436% 18.481%)"); }); @@ -371,6 +419,7 @@ describe("bgPositive color", () => { const { bgPositive } = new LightModeTheme( "oklch(0.62 0.19 100)", ).getColors(); + expect(bgPositive).toEqual("rgb(6.7436% 63.436% 18.481%)"); }); @@ -378,6 +427,7 @@ describe("bgPositive color", () => { const { bgPositive } = new LightModeTheme( "oklch(0.62 0.1 100)", ).getColors(); + expect(bgPositive).toEqual("rgb(6.7436% 63.436% 18.481%)"); }); }); @@ -387,6 +437,7 @@ describe("bgPositiveHover color", () => { const { bgPositiveHover } = new LightModeTheme( "oklch(0.62 0.19 100)", ).getColors(); + expect(bgPositiveHover).toEqual("rgb(18.172% 69.721% 25.266%)"); }); }); @@ -396,6 +447,7 @@ describe("bgPositiveActive color", () => { const { bgPositiveActive } = new LightModeTheme( "oklch(0.62 0.19 100)", ).getColors(); + expect(bgPositiveActive).toEqual("rgb(0% 60.947% 15.563%)"); }); }); @@ -405,6 +457,7 @@ describe("bgPositiveSubtle color", () => { const { bgPositiveSubtle } = new LightModeTheme( "oklch(0.62 0.19 100)", ).getColors(); + expect(bgPositiveSubtle).toEqual("rgb(81.329% 100% 81.391%)"); }); }); @@ -414,6 +467,7 @@ describe("bgPositiveSubtleHover color", () => { const { bgPositiveSubtleHover } = new LightModeTheme( "oklch(0.62 0.19 100)", ).getColors(); + expect(bgPositiveSubtleHover).toEqual("rgb(83.899% 100% 83.95%)"); }); }); @@ -423,6 +477,7 @@ describe("bgPositiveSubtleActive color", () => { const { bgPositiveSubtleActive } = new LightModeTheme( "oklch(0.62 0.19 100)", ).getColors(); + expect(bgPositiveSubtleActive).toEqual("rgb(80.049% 98.746% 80.116%)"); }); }); @@ -432,11 +487,13 @@ describe("bgNegative color", () => { const { bgNegative } = new LightModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegative).toEqual("rgb(82.941% 0.97856% 21.484%)"); }); it("should return correct color when seed color is red (hue between 5 and 49) but chroma is not greater than 0.12", () => { const { bgNegative } = new LightModeTheme("oklch(0.55 0.1 27)").getColors(); + expect(bgNegative).toEqual("rgb(83.108% 4.6651% 10.252%)"); }); @@ -444,11 +501,13 @@ describe("bgNegative color", () => { const { bgNegative } = new LightModeTheme( "oklch(0.55 0.22 60)", ).getColors(); + expect(bgNegative).toEqual("rgb(83.108% 4.6651% 10.252%)"); }); it("should return correct color when seed color is not red (hue outside 5-49) and chroma is not greater than 0.12", () => { const { bgNegative } = new LightModeTheme("oklch(0.55 0.1 60)").getColors(); + expect(bgNegative).toEqual("rgb(83.108% 4.6651% 10.252%)"); }); }); @@ -458,6 +517,7 @@ describe("bgNegativeHover color", () => { const { bgNegativeHover } = new LightModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeHover).toEqual("rgb(90.138% 15.796% 27.164%)"); }); }); @@ -467,6 +527,7 @@ describe("bgNegativeActive color", () => { const { bgNegativeActive } = new LightModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeActive).toEqual("rgb(80.074% 0% 19.209%)"); }); }); @@ -476,6 +537,7 @@ describe("bgNegativeSubtle color", () => { const { bgNegativeSubtle } = new LightModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeSubtle).toEqual("rgb(100% 88.914% 88.427%)"); }); }); @@ -485,6 +547,7 @@ describe("bgNegativeSubtleHover color", () => { const { bgNegativeSubtleHover } = new LightModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeSubtleHover).toEqual("rgb(100% 92.552% 92.132%)"); }); }); @@ -494,6 +557,7 @@ describe("bgNegativeSubtleActive color", () => { const { bgNegativeSubtleActive } = new LightModeTheme( "oklch(0.55 0.22 27)", ).getColors(); + expect(bgNegativeSubtleActive).toEqual("rgb(100% 87.314% 86.814%)"); }); }); @@ -501,21 +565,25 @@ describe("bgNegativeSubtleActive color", () => { describe("bgWarning color", () => { it("should return correct color when seed color is yellow (hue between 60 and 115) and chroma > 0.09", () => { const { bgWarning } = new LightModeTheme("oklch(0.75 0.15 85)").getColors(); + expect(bgWarning).toEqual("rgb(91.527% 60.669% 16.491%)"); }); it("should return correct color when seed color is yellow (hue between 60 and 115) but chroma is not greater than 0.09", () => { const { bgWarning } = new LightModeTheme("oklch(0.75 0.05 85)").getColors(); + expect(bgWarning).toEqual("rgb(85.145% 64.66% 8.0285%)"); }); it("should return correct color when seed color is not yellow (hue outside 60-115) and chroma > 0.09", () => { const { bgWarning } = new LightModeTheme("oklch(0.75 0.15 85)").getColors(); + expect(bgWarning).toEqual("rgb(91.527% 60.669% 16.491%)"); }); it("should return correct color when seed color is not yellow (hue outside 60-115) and chroma is not greater than 0.09", () => { const { bgWarning } = new LightModeTheme("oklch(0.75 0.05 85)").getColors(); + expect(bgWarning).toEqual("rgb(85.145% 64.66% 8.0285%)"); }); }); @@ -525,6 +593,7 @@ describe("bgWarningHover color", () => { const { bgWarningHover } = new LightModeTheme( "oklch(0.75 0.15 85)", ).getColors(); + expect(bgWarningHover).toEqual("rgb(95.533% 64.413% 21.716%)"); }); }); @@ -534,6 +603,7 @@ describe("bgWarningActive color", () => { const { bgWarningActive } = new LightModeTheme( "oklch(0.75 0.15 85)", ).getColors(); + expect(bgWarningActive).toEqual("rgb(90.198% 59.428% 14.545%)"); }); }); @@ -543,6 +613,7 @@ describe("bgWarningSubtle color", () => { const { bgWarningSubtle } = new LightModeTheme( "oklch(0.75 0.15 85)", ).getColors(); + expect(bgWarningSubtle).toEqual("rgb(100% 92.843% 80.874%)"); }); }); @@ -552,6 +623,7 @@ describe("bgWarningSubtleHover color", () => { const { bgWarningSubtleHover } = new LightModeTheme( "oklch(0.75 0.15 85)", ).getColors(); + expect(bgWarningSubtleHover).toEqual("rgb(100% 95.606% 84.445%)"); }); }); @@ -561,6 +633,7 @@ describe("bgWarningSubtleActive color", () => { const { bgWarningSubtleActive } = new LightModeTheme( "oklch(0.75 0.15 85)", ).getColors(); + expect(bgWarningSubtleActive).toEqual("rgb(100% 91.541% 79.601%)"); }); }); @@ -760,6 +833,7 @@ describe("fgOnWarning color ", () => { describe("bd color", () => { it("should return correct color", () => { const { bd } = new LightModeTheme("oklch(0.45 0.5 60)").getColors(); + expect(bd).toEqual("rgb(75.553% 74.037% 72.885%)"); }); }); @@ -767,11 +841,13 @@ describe("bd color", () => { describe("bdAccent color", () => { it("should return correct color when chroma < 0.04", () => { const { bdAccent } = new LightModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdAccent).toEqual("rgb(38.473% 32.008% 26.943%)"); }); it("should return correct color when chroma > 0.04", () => { const { bdAccent } = new LightModeTheme("oklch(0.45 0.1 60)").getColors(); + expect(bdAccent).toEqual("rgb(48.857% 27.291% 4.3335%)"); }); }); @@ -779,26 +855,31 @@ describe("bdAccent color", () => { describe("bdFocus color", () => { it("should return correct color when lightness < 0.6", () => { const { bdFocus } = new LightModeTheme("oklch(0.45 0.4 60)").getColors(); + expect(bdFocus).toEqual("rgb(68.381% 33.855% 0%)"); }); it("should return correct color when lightness > 0.8", () => { const { bdFocus } = new LightModeTheme("oklch(0.85 0.03 60)").getColors(); + expect(bdFocus).toEqual("rgb(68.313% 33.908% 0%)"); }); it("should return correct color when chroma < 0.15", () => { const { bdFocus } = new LightModeTheme("oklch(0.85 0.1 60)").getColors(); + expect(bdFocus).toEqual("rgb(68.313% 33.908% 0%)"); }); it("should return correct color when hue is between 0 and 55", () => { const { bdFocus } = new LightModeTheme("oklch(0.85 0.1 30)").getColors(); + expect(bdFocus).toEqual("rgb(72.468% 27.962% 22.197%)"); }); it("should return correct color when hue > 340", () => { const { bdFocus } = new LightModeTheme("oklch(0.85 0.1 350)").getColors(); + expect(bdFocus).toEqual("rgb(68.494% 27.322% 49.304%)"); }); }); @@ -806,6 +887,7 @@ describe("bdFocus color", () => { describe("bdNeutral color", () => { it("should return correct color when chroma < 0.04", () => { const { bdNeutral } = new LightModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdNeutral).toEqual("rgb(33.384% 33.384% 33.384%)"); }); }); @@ -815,6 +897,7 @@ describe("bdNeutralHover", () => { const { bdNeutralHover } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdNeutralHover).toEqual("rgb(62.05% 62.05% 62.05%)"); }); }); @@ -824,6 +907,7 @@ describe("bdPositive", () => { const { bdPositive } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdPositive).toEqual("rgb(37.9% 86.464% 41.821%)"); }); }); @@ -833,6 +917,7 @@ describe("bdPositiveHover", () => { const { bdPositiveHover } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdPositiveHover).toEqual("rgb(51.497% 99.713% 54.439%)"); }); }); @@ -842,6 +927,7 @@ describe("bdNegative", () => { const { bdNegative } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdNegative).toEqual("rgb(100% 55.521% 50.654%)"); }); }); @@ -851,6 +937,7 @@ describe("bdNegativeHover", () => { const { bdNegativeHover } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdNegativeHover).toEqual("rgb(100% 72.272% 68.285%)"); }); }); @@ -858,6 +945,7 @@ describe("bdNegativeHover", () => { describe("bdWarning", () => { it("should return correct color", () => { const { bdWarning } = new LightModeTheme("oklch(0.45 0.03 60)").getColors(); + expect(bdWarning).toEqual("rgb(94.272% 73.467% 23.044%)"); }); }); @@ -867,6 +955,7 @@ describe("bdWarningHover", () => { const { bdWarningHover } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdWarningHover).toEqual("rgb(100% 86.971% 47.122%)"); }); }); @@ -876,6 +965,7 @@ describe("bdOnAccent", () => { const { bdOnAccent } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnAccent).toEqual("rgb(5.2437% 1.364% 0%)"); }); }); @@ -885,6 +975,7 @@ describe("bdOnNeutral", () => { const { bdOnNeutral } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnNeutral).toEqual("rgb(46.751% 46.751% 46.751%)"); }); }); @@ -894,6 +985,7 @@ describe("bdOnPositive", () => { const { bdOnPositive } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnPositive).toEqual("rgb(0% 22.253% 0%)"); }); }); @@ -903,6 +995,7 @@ describe("bdOnNegative", () => { const { bdOnNegative } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnNegative).toEqual("rgb(25.15% 0% 0%)"); }); }); @@ -912,6 +1005,7 @@ describe("bdOnWarning", () => { const { bdOnWarning } = new LightModeTheme( "oklch(0.45 0.03 60)", ).getColors(); + expect(bdOnWarning).toEqual("rgb(40.354% 27.735% 0%)"); }); }); diff --git a/app/client/packages/design-system/theming/src/hooks/src/useCssTokens.tsx b/app/client/packages/design-system/theming/src/hooks/src/useCssTokens.tsx index b5e30df72fe..99a69e87760 100644 --- a/app/client/packages/design-system/theming/src/hooks/src/useCssTokens.tsx +++ b/app/client/packages/design-system/theming/src/hooks/src/useCssTokens.tsx @@ -18,6 +18,7 @@ const getTypographyCss = (typography: Typography) => { ${objectKeys(typography).reduce((prev, key) => { const currentKey = key as keyof Typography; const { after, before, fontSize, lineHeight } = typography[currentKey]; + return ( prev + ` diff --git a/app/client/packages/design-system/widgets-old/src/Button/index.tsx b/app/client/packages/design-system/widgets-old/src/Button/index.tsx index a531ccc0608..e7853d48f26 100644 --- a/app/client/packages/design-system/widgets-old/src/Button/index.tsx +++ b/app/client/packages/design-system/widgets-old/src/Button/index.tsx @@ -305,6 +305,7 @@ const btnColorStyles = (props: ButtonProps, state: string): BtnColorType => { txtColor = "", border = "", outline = ""; + switch (props.category) { case Category.primary: bgColor = stateStyles(props, state).bgColorPrimary; @@ -324,6 +325,7 @@ const btnColorStyles = (props: ButtonProps, state: string): BtnColorType => { outline = "2px solid var(--ads-color-blue-150)"; break; } + return { bgColor, txtColor, border, outline }; }; @@ -514,6 +516,7 @@ const getButtonContent = (props: ButtonProps) => { : props.tag === "a" ? IconPositions.right : IconPositions.left; + return ( <> {iconPos === IconPositions.left && getIconContent(props)} @@ -527,6 +530,7 @@ const getButtonContent = (props: ButtonProps) => { function ButtonComponent(props: ButtonProps) { const { className, cypressSelector, isLoading, onClick } = props; const filteredProps = _.omit(props, ["fill"]); + return ( { if (props.backgroundColor) return props.backgroundColor; + props.checked ? "var(--ads-color-black-900)" : "var(--ads-color-black-0)"; }}; border: 1.4px solid; border-color: ${(props) => { if (props.borderColor) return props.borderColor; + props.checked ? "var(--ads-color-black-900)" : "var(--ads-color-black-400)"; }}; flex: 0 0 auto; @@ -231,6 +233,7 @@ const Selected = styled.div<{ } else if (props.hasError) { return "var(--ads-old-color-fair-pink)"; } + return props.bgColor || "var(--ads-color-black-0)"; }}; pointer-events: ${(props) => (props.disabled ? "none" : "auto")}; @@ -673,6 +676,7 @@ function TooltipWrappedText( ) { const { label, ...textProps } = props; const targetRef = useRef(null); + return ( { event.stopPropagation(); + if (removeSelectedOptionClickHandler) { removeSelectedOptionClickHandler(s as DropdownOption); } @@ -838,6 +843,7 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) { ); }, ); + setSearchValue(searchStr); setOptions(filteredOptions); onSearch && onSearch(searchStr); @@ -882,6 +888,7 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) { > {options.map((option: DropdownOption, index: number) => { let isSelected = false; + if ( props.isMultiSelect && Array.isArray(props.selected) && @@ -894,6 +901,7 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) { isSelected = (props.selected as DropdownOption).value === option.value; } + if (renderOption) { return renderOption({ option, @@ -904,6 +912,7 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) { isHighlighted: index === props.highlightIndex, }); } + return !option.isSectionHeader ? ( { setSelected(props.selected); + if (!props.isMultiSelect) closeIfOpen(); }, [props.selected]); @@ -1070,6 +1080,7 @@ export default function Dropdown(props: DropdownProps) { ...(selected as DropdownOption[]), option, ]; + setSelected(newOptions); setIsOpen(true); } @@ -1078,6 +1089,7 @@ export default function Dropdown(props: DropdownProps) { setSelected(option); setIsOpen(false); } + onSelect && onSelect(option.value, option, isUpdatedViaKeyboard); option.onSelect && option.onSelect(option.value, option); }, @@ -1088,11 +1100,13 @@ export default function Dropdown(props: DropdownProps) { const removeSelectedOptionClickHandler = useCallback( (optionToBeRemoved: DropdownOption) => { let selectedOptions: DropdownOption | DropdownOption[] = []; + if (props.isMultiSelect) { setIsOpen(true); } else { setIsOpen(false); } + if (!Array.isArray(selected)) { if (optionToBeRemoved.value === selected.value) { selectedOptions = optionToBeRemoved; @@ -1102,6 +1116,7 @@ export default function Dropdown(props: DropdownProps) { (option: DropdownOption) => option.value !== optionToBeRemoved.value, ); } + setSelected(selectedOptions); removeSelectedOption && removeSelectedOption(optionToBeRemoved.value, optionToBeRemoved); @@ -1126,20 +1141,25 @@ export default function Dropdown(props: DropdownProps) { const elementList = document.getElementById( "ds--dropdown-options", )?.children; + if (!elementList || elementList?.length === 0) { setHighlight(-1); } + switch (e.key) { case "Escape": emitKeyPressEvent(dropdownWrapperRef.current, e.key); + if (isOpen) { setSelected((prevSelected) => { if (prevSelected != props.selected) return props.selected; + return prevSelected; }); setIsOpen(false); e.nativeEvent.stopImmediatePropagation(); } + break; case " ": if (!isOpen) { @@ -1147,16 +1167,20 @@ export default function Dropdown(props: DropdownProps) { onClickHandler(); break; } + if (!props.enableSearch) { emitKeyPressEvent(dropdownWrapperRef.current, e.key); + if (closeOnSpace) { e.preventDefault(); + if (isOpen) { if (highlight !== -1 && elementList) { const optionElement = elementList[highlight] as HTMLElement; const dropdownOptionElement = optionElement.querySelector( ".t--dropdown-option", ) as HTMLElement; + dropdownOptionElement && typeof dropdownOptionElement.click === "function" ? dropdownOptionElement.click() @@ -1167,16 +1191,19 @@ export default function Dropdown(props: DropdownProps) { } } } + break; case "Enter": emitKeyPressEvent(dropdownWrapperRef.current, e.key); e.preventDefault(); + if (isOpen) { if (highlight !== -1 && elementList) { const optionElement = elementList[highlight] as HTMLElement; const dropdownOptionElement = optionElement.querySelector( ".t--dropdown-option", ) as HTMLElement; + dropdownOptionElement && typeof dropdownOptionElement.click === "function" ? dropdownOptionElement.click() @@ -1185,6 +1212,7 @@ export default function Dropdown(props: DropdownProps) { } else { onClickHandler(); } + break; case "ArrowUp": if (!isOpen) { @@ -1192,19 +1220,24 @@ export default function Dropdown(props: DropdownProps) { onClickHandler(); break; } + if (elementList) { emitKeyPressEvent(dropdownWrapperRef.current, e.key); e.preventDefault(); + if (highlight === -1) { setHighlight(elementList.length - 1); } else { setHighlight((x) => { const index = x - 1 < 0 ? elementList.length - 1 : x - 1; + elementList[index]?.scrollIntoView(scrollIntoViewOptions); + return index; }); } } + break; case "ArrowDown": if (!isOpen) { @@ -1212,28 +1245,35 @@ export default function Dropdown(props: DropdownProps) { onClickHandler(); break; } + if (elementList) { emitKeyPressEvent(dropdownWrapperRef.current, e.key); e.preventDefault(); + if (highlight === -1) { setHighlight(0); } else { setHighlight((x) => { const index = x + 1 > elementList.length - 1 ? 0 : x + 1; + elementList[index]?.scrollIntoView(scrollIntoViewOptions); + return index; }); } } + break; case "Tab": emitKeyPressEvent( dropdownWrapperRef.current, `${e.shiftKey ? "Shift+" : ""}${e.key}`, ); + if (isOpen) { setIsOpen(false); } + break; } }, @@ -1250,6 +1290,7 @@ export default function Dropdown(props: DropdownProps) { requestAnimationFrame(() => { if (dropdownWrapperRef.current) { const width = entries[0].borderBoxSize?.[0].inlineSize; + if (typeof width === "number" && width !== prevWidth.current) { prevWidth.current = width; setDropdownWrapperWidth(`${width}px`); @@ -1262,6 +1303,7 @@ export default function Dropdown(props: DropdownProps) { useEffect(() => { const resizeObserver = new ResizeObserver(onParentResize); + if (dropdownWrapperRef.current && props.fillOptions) resizeObserver.observe(dropdownWrapperRef.current); @@ -1271,6 +1313,7 @@ export default function Dropdown(props: DropdownProps) { }, [dropdownWrapperRef.current, props.fillOptions]); let dropdownHeight = props.isMultiSelect ? "auto" : "36px"; + if (props.height) { dropdownHeight = props.height; } diff --git a/app/client/packages/design-system/widgets-old/src/Icon/index.tsx b/app/client/packages/design-system/widgets-old/src/Icon/index.tsx index a6b5449dd8a..d8881a2e5e1 100644 --- a/app/client/packages/design-system/widgets-old/src/Icon/index.tsx +++ b/app/client/packages/design-system/widgets-old/src/Icon/index.tsx @@ -687,6 +687,7 @@ export const IconWrapper = styled.span` function getControlIcon(iconName: string) { const ControlIcon = ControlIcons[iconName]; + return ; } @@ -944,6 +945,7 @@ const Icon = forwardRef( const clickable = props.clickable === undefined ? true : props.clickable; let loader = ; + if (props.loaderWithIconWrapper) { loader = ( diff --git a/app/client/packages/design-system/widgets-old/src/LabelWithTooltip/index.tsx b/app/client/packages/design-system/widgets-old/src/LabelWithTooltip/index.tsx index 452d57fcc6a..bec2a8c4aa6 100644 --- a/app/client/packages/design-system/widgets-old/src/LabelWithTooltip/index.tsx +++ b/app/client/packages/design-system/widgets-old/src/LabelWithTooltip/index.tsx @@ -104,14 +104,19 @@ export const labelLayoutStyles = css<{ display: flex; flex-direction: ${({ compactMode, labelPosition }) => { if (labelPosition === LabelPosition.Left) return "row"; + if (labelPosition === LabelPosition.Top) return "column"; + if (compactMode) return "row"; + return "column"; }}; align-items: ${({ compactMode, labelPosition }) => { if (labelPosition === LabelPosition.Top) return "flex-start"; + if (compactMode) return "center"; + return "flex-start"; }}; justify-content: flex-start; @@ -126,8 +131,11 @@ export const multiSelectInputContainerStyles = css<{ display: flex; align-items: ${({ compactMode, labelPosition }) => { if (labelPosition === LabelPosition.Top) return "flex-start"; + if (labelPosition === LabelPosition.Left) return "center"; + if (compactMode) return "center"; + return "flex-start"; }}; `; @@ -174,11 +182,13 @@ export const StyledLabel = styled(Label)` &&& { ${({ compact, hasHelpText, position }) => { if (!position && !compact) return; + if ( position === LabelPosition.Left || ((!position || position === LabelPosition.Auto) && compact) ) return `margin-bottom: 0px; margin-right: ${LABEL_DEFAULT_GAP}`; + return `margin-bottom: ${LABEL_DEFAULT_GAP}; ${ hasHelpText ? `margin-right: ${LABEL_DEFAULT_GAP}` : "margin-right: 0px" }`; @@ -215,7 +225,9 @@ const ToolTipIcon = styled(IconWrapper)` if (position === LabelPosition.Top) { return `margin-bottom: ${LABEL_DEFAULT_GAP}`; } + if (compact || position === LabelPosition.Left) return "margin-bottom: 0px"; + return `margin-bottom: ${LABEL_DEFAULT_GAP}`; }}; `; diff --git a/app/client/packages/design-system/widgets-old/src/ScrollIndicator/index.tsx b/app/client/packages/design-system/widgets-old/src/ScrollIndicator/index.tsx index 0cad3293d9b..e72a7017728 100644 --- a/app/client/packages/design-system/widgets-old/src/ScrollIndicator/index.tsx +++ b/app/client/packages/design-system/widgets-old/src/ScrollIndicator/index.tsx @@ -52,6 +52,7 @@ interface Props { showScrollbarOnlyOnHover?: boolean; mode?: "DARK" | "LIGHT"; } + function ScrollIndicator({ alwaysShowScrollbar, bottom, @@ -102,10 +103,12 @@ function ScrollIndicator({ const thumbHeight = e.target.offsetHeight / (e.target.scrollHeight / e.target.offsetHeight); const thumbPosition = (e.target.scrollTop / e.target.offsetHeight) * 100; + /* set scroll thumb height */ if (thumbRef.current) { thumbRef.current.style.height = thumbHeight + "px"; } + setThumbPosition({ thumbPosition, }); @@ -128,6 +131,7 @@ function ScrollIndicator({ const setScrollVisibilityOnHover = useCallback((e) => { if (e?.type === "mouseenter") { setIsScrollVisible(true); + /* Scroll Thumb by default has height 0. Since we have to rely on hover event instead of scroll event when showScrollbarOnlyOnHover is true, @@ -154,6 +158,7 @@ function ScrollIndicator({ setScrollVisibilityOnHover, ); } + return () => { if (showScrollbarOnlyOnHover) { containerRef.current?.removeEventListener( diff --git a/app/client/packages/design-system/widgets-old/src/SearchComponent/index.tsx b/app/client/packages/design-system/widgets-old/src/SearchComponent/index.tsx index 988a5bd9156..ab51942ff46 100644 --- a/app/client/packages/design-system/widgets-old/src/SearchComponent/index.tsx +++ b/app/client/packages/design-system/widgets-old/src/SearchComponent/index.tsx @@ -106,6 +106,7 @@ class SearchComponent extends React.Component< | React.ChangeEvent, ) => { const search = event.target.value; + this.setState({ localValue: search }); this.onDebouncedSearch(search); }; diff --git a/app/client/packages/design-system/widgets-old/src/TableDropdown/index.tsx b/app/client/packages/design-system/widgets-old/src/TableDropdown/index.tsx index bf7aab63c0b..341020e2922 100644 --- a/app/client/packages/design-system/widgets-old/src/TableDropdown/index.tsx +++ b/app/client/packages/design-system/widgets-old/src/TableDropdown/index.tsx @@ -106,6 +106,7 @@ function TableDropdown(props: DropdownProps) { setSelectedOption(props.options[index]); onSelect && onSelect(props.options[index]); } + setIsDropdownOpen(false); }; diff --git a/app/client/packages/design-system/widgets-old/src/Text/index.tsx b/app/client/packages/design-system/widgets-old/src/Text/index.tsx index 8872278b443..d8db4243a67 100644 --- a/app/client/packages/design-system/widgets-old/src/Text/index.tsx +++ b/app/client/packages/design-system/widgets-old/src/Text/index.tsx @@ -46,6 +46,7 @@ export type TextProps = CommonComponentProps & { const typeSelector = (props: TextProps): string => { let color = ""; + switch (props.type) { case TextType.P0: color = "var(--ads-text-color)"; @@ -63,6 +64,7 @@ const typeSelector = (props: TextProps): string => { color = "var(--ads-text-heading-color)"; break; } + return color; }; diff --git a/app/client/packages/design-system/widgets-old/src/Tooltip/index.tsx b/app/client/packages/design-system/widgets-old/src/Tooltip/index.tsx index d5af97850b0..8555bc1cb1e 100644 --- a/app/client/packages/design-system/widgets-old/src/Tooltip/index.tsx +++ b/app/client/packages/design-system/widgets-old/src/Tooltip/index.tsx @@ -67,6 +67,7 @@ const TooltipWrapper = styled(Tooltip)< if (!portalContainer) { const tooltipPortalElement = document.createElement("div"); + tooltipPortalElement.id = rootElementId; document.body.append(tooltipPortalElement); portalContainer = document.getElementById(rootElementId); diff --git a/app/client/packages/design-system/widgets-old/src/utils/colors.ts b/app/client/packages/design-system/widgets-old/src/utils/colors.ts index 1d9d2ee2657..0f2220a6b36 100644 --- a/app/client/packages/design-system/widgets-old/src/utils/colors.ts +++ b/app/client/packages/design-system/widgets-old/src/utils/colors.ts @@ -7,6 +7,7 @@ function validateHex(arg: string) { if (regex.test(arg)) return arg; else { const cssVariable = arg.substring(4, arg.length - 1); + return getComputedStyle(document.documentElement).getPropertyValue( cssVariable, ); @@ -22,6 +23,7 @@ export const hexToRgb = ( } => { const validatedHex = validateHex(hex); const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(validatedHex); + return result ? { r: parseInt(result[1], 16), @@ -36,6 +38,7 @@ export const hexToRgb = ( }; export const hexToRgba = (color: string, alpha: number) => { const value = hexToRgb(color); + return `rgba(${value.r}, ${value.g}, ${value.b}, ${alpha});`; }; export const lighten = (color: string, amount: number) => { diff --git a/app/client/packages/design-system/widgets-old/src/utils/icon-loadables.tsx b/app/client/packages/design-system/widgets-old/src/utils/icon-loadables.tsx index 1845fd0136e..20829d97a99 100644 --- a/app/client/packages/design-system/widgets-old/src/utils/icon-loadables.tsx +++ b/app/client/packages/design-system/widgets-old/src/utils/icon-loadables.tsx @@ -40,6 +40,7 @@ export function importSvg( .then((m) => ({ default: m.ReactComponent })) .catch((e) => { log.warn("Failed to load SVG icon:", e); + return { default: IconLoadFailFallback }; }), ); @@ -51,6 +52,7 @@ export function importRemixIcon( return importIconImpl(async () => importFn().catch((e) => { log.warn("Failed to load SVG icon:", e); + return { default: IconLoadFailFallback }; }), ); diff --git a/app/client/packages/design-system/widgets/src/components/Button/tests/Button.test.tsx b/app/client/packages/design-system/widgets/src/components/Button/tests/Button.test.tsx index 8854731892e..da7ab43f915 100644 --- a/app/client/packages/design-system/widgets/src/components/Button/tests/Button.test.tsx +++ b/app/client/packages/design-system/widgets/src/components/Button/tests/Button.test.tsx @@ -35,6 +35,7 @@ describe("@appsmith/wds/Button", () => { // eslint-disable-next-line testing-library/no-node-access const icon = screen.getByRole("button").querySelector("[data-icon]"); + expect(icon).toBeInTheDocument(); }); @@ -43,12 +44,14 @@ describe("@appsmith/wds/Button", () => { // Note: using testid=t--fallack-icon as the icon is rendered lazily and the fallback component // has a testid const icon = screen.getByTestId("t--fallback-icon"); + expect(icon).toBeInTheDocument(); }); it("sets icon position attribute based on the prop ", () => { render(
)); + return ( ; } diff --git a/app/client/src/ce/pages/Upgrade/UpgradePage.tsx b/app/client/src/ce/pages/Upgrade/UpgradePage.tsx index 9c0fb92be53..a8671f4aa88 100644 --- a/app/client/src/ce/pages/Upgrade/UpgradePage.tsx +++ b/app/client/src/ce/pages/Upgrade/UpgradePage.tsx @@ -19,6 +19,7 @@ export const Container = styled.div` export default function UpgradePage(props: UpgradePageProps) { const { carousel, footer, header } = props; + return ( { // eslint-disable-next-line const { searchedPackages } = props; + return null; }; diff --git a/app/client/src/ce/pages/common/WorkflowSearchItem.tsx b/app/client/src/ce/pages/common/WorkflowSearchItem.tsx index 2ff7ab13c84..57da1d52da9 100644 --- a/app/client/src/ce/pages/common/WorkflowSearchItem.tsx +++ b/app/client/src/ce/pages/common/WorkflowSearchItem.tsx @@ -7,6 +7,7 @@ interface Props { const WorkflowSearchItem = (props: Props) => { // eslint-disable-next-line const { workflowsList } = props; + return null; }; diff --git a/app/client/src/ce/pages/workspace/InviteUsersForm.tsx b/app/client/src/ce/pages/workspace/InviteUsersForm.tsx index 9b2a32c425f..319882bf8c8 100644 --- a/app/client/src/ce/pages/workspace/InviteUsersForm.tsx +++ b/app/client/src/ce/pages/workspace/InviteUsersForm.tsx @@ -170,6 +170,7 @@ const validate = (values: any) => { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any const errors: any = {}; + if (!(values.users && values.users.length > 0)) { errors["users"] = createMessage(INVITE_USERS_VALIDATION_EMAILS_EMPTY); } @@ -190,6 +191,7 @@ const validate = (values: any) => { } }); } + return errors; }; @@ -205,6 +207,7 @@ export function InviteUserText({ const rampLink = useSelector(rampLinkSelector); const showRampSelector = showProductRamps(RAMP_NAME.INVITE_USER_TO_APP); const canShowRamp = useSelector(showRampSelector); + return ( ); + return ( option.value) .join(",") : selectedOption[0].value; + validateFormValues({ ...values, role: roles }); const usersAsStringsArray = values.users.split(","); + // update state to show success message correctly updateNumberOfUsersInvited(usersAsStringsArray.length); const validEmails = usersAsStringsArray.filter((user: string) => diff --git a/app/client/src/ce/pages/workspace/Members.tsx b/app/client/src/ce/pages/workspace/Members.tsx index a00c727d487..19d5b4224ca 100644 --- a/app/client/src/ce/pages/workspace/Members.tsx +++ b/app/client/src/ce/pages/workspace/Members.tsx @@ -237,6 +237,7 @@ export default function MemberSettings(props: PageProps) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const onDeleteMember = (data?: any) => { if (!userToBeDeleted && !data) return null; + dispatch( deleteWorkspaceUser( userToBeDeleted?.workspaceId || data?.workspaceId, @@ -271,6 +272,7 @@ export default function MemberSettings(props: PageProps) { const userBeingDeleted = allUsers.find( (user) => user.username === userToBeDeleted.username, ); + if (!userBeingDeleted) { setUserToBeDeleted(null); onCloseConfirmationModal(); @@ -314,6 +316,7 @@ export default function MemberSettings(props: PageProps) { useEffect(() => { if (searchValue) { const filteredUsers = getFilteredUsers(); + setFilteredData(filteredUsers); } else { setFilteredData(membersData); @@ -330,6 +333,7 @@ export default function MemberSettings(props: PageProps) { // eslint-disable-next-line @typescript-eslint/no-explicit-any Cell: function UserCell(props: any) { const member = props.cell.row.original; + return ( <> @@ -390,6 +394,7 @@ export default function MemberSettings(props: PageProps) { role.value?.split(" - ")[0] === cellProps.cell.value?.split(" - ")[0], ); + if (data.username === currentUser?.username) { return ( @@ -397,6 +402,7 @@ export default function MemberSettings(props: PageProps) { ); } + return ( { const index = field.name * 1; + return index >= 1; }, indentWidth: 1, diff --git a/app/client/src/pages/Editor/CustomWidgetBuilder/Preview/Debugger/objectView.tsx b/app/client/src/pages/Editor/CustomWidgetBuilder/Preview/Debugger/objectView.tsx index 552d2f4da0f..bb56ff9ef61 100644 --- a/app/client/src/pages/Editor/CustomWidgetBuilder/Preview/Debugger/objectView.tsx +++ b/app/client/src/pages/Editor/CustomWidgetBuilder/Preview/Debugger/objectView.tsx @@ -18,6 +18,7 @@ export default function ObjectView(props: { // eslint-disable-next-line @typescript-eslint/no-explicit-any shouldCollapse: (field: any) => { const index = field.name * 1; + return index >= 2; }, indentWidth: 1, diff --git a/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx index e6670a12e37..2c79bb0e75f 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx @@ -53,6 +53,7 @@ class DatasourceDBEditor extends JSONtoForm { const plugin = appState.entities.plugins.list.find( (plugin) => plugin.id === this.props.datasource?.pluginId, ); + if (!!plugin) openDoc(DocsLink.WHITELIST_IP, plugin?.documentationLink, plugin?.name); else openDoc(DocsLink.WHITELIST_IP); diff --git a/app/client/src/pages/Editor/DataSourceEditor/DSFormHeader.tsx b/app/client/src/pages/Editor/DataSourceEditor/DSFormHeader.tsx index 2c263cdaaaa..34561ee1f52 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DSFormHeader.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DSFormHeader.tsx @@ -121,6 +121,7 @@ export const DSFormHeader = (props: DSFormHeaderProps) => { const deleteAction = () => { if (isDeleting) return; + AnalyticsUtil.logEvent("DATASOURCE_CARD_DELETE_ACTION"); dispatch(deleteDatasource({ id: datasourceId })); }; @@ -138,6 +139,7 @@ export const DSFormHeader = (props: DSFormHeaderProps) => { onSelect={(e: Event) => { e.preventDefault(); e.stopPropagation(); + if (!isDeleting) { confirmDelete ? deleteAction() : setConfirmDelete(true); } diff --git a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx index e09cacd3a59..478693bb169 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx @@ -99,6 +99,7 @@ const renderKVArray = ( { configProperty, label }: { configProperty: string; label: string }, ) => { const configPropertyKey = configProperty.split("[*].")[1]; + // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any values.forEach((value: any, index: number) => { @@ -112,10 +113,12 @@ const renderKVArray = ( value: value[configPropertyKey], }); }); + return acc; }, [], ); + return renderValues.map((renderValue, index: number) => ( {renderValue.map(({ key, label, value }) => ( @@ -151,6 +154,7 @@ export function renderDatasourceSection( ) ) return null; + if ("children" in section) { if (isKVArray(section.children)) { return renderKVArray( @@ -177,6 +181,7 @@ export function renderDatasourceSection( const customConfigProperty = `datasourceStorages.${currentEnvironment}.` + configProperty; const reactKey = datasource.id + "_" + label; + if (controlType === "FIXED_KEY_INPUT") { return ( @@ -195,6 +200,7 @@ export function renderDatasourceSection( // eslint-disable-next-line @typescript-eslint/no-explicit-any (el: any) => el.value === value, ); + if (option && option.label) { value = option.label; } @@ -327,6 +333,7 @@ class RenderDatasourceInformation extends React.Component { ? false : isMultipleEnvEnabled(selectFeatureFlags(state)); const currentEnvironmentId = getCurrentEnvironmentId(state); + return { currentEnv: isEnvEnabled ? currentEnvironmentId : getDefaultEnvId(), isEnvEnabled, diff --git a/app/client/src/pages/Editor/DataSourceEditor/FormTitle.tsx b/app/client/src/pages/Editor/DataSourceEditor/FormTitle.tsx index d42c474cc7f..81253de14b6 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/FormTitle.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/FormTitle.tsx @@ -54,6 +54,7 @@ function FormTitle(props: FormTitleProps) { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any const datasourcesNames: Record = {}; + datasources // in case of REST API and Authenticated GraphQL API, when user clicks on save as datasource // we first need to update the action and then redirect to action page, @@ -89,6 +90,7 @@ function FormTitle(props: FormTitleProps) { } else if (hasNameConflict(name)) { return `${name} is already being used or is a restricted keyword.`; } + return false; }, [hasNameConflict], @@ -98,6 +100,7 @@ function FormTitle(props: FormTitleProps) { (name: string) => { // Check if the datasource name equals "Untitled datasource ABC" if no , use the name passed. const datsourceName = name || "Untitled datasource ABC"; + if ( !isInvalidDatasourceName(name) && currentDatasource && diff --git a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx index 5acada00fd0..2c7e286333f 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx @@ -92,6 +92,7 @@ export class JSONtoForm< ) ) return null; + return ( this.props.setupConfig(c)); + // We pass last child for legacy reasons, to keep the logic here exactly same as before. return this.renderSingleConfig(children[children.length - 1], children); } catch (e) { @@ -165,13 +169,16 @@ export class JSONtoForm< ) ) return null; + if ("children" in propertyControlOrSection) { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any const { children } = propertyControlOrSection as any; + if (isKVArray(children)) { return this.renderKVArray(children); } + return this.renderEachConfig(propertyControlOrSection); } else { return this.renderSingleConfig(propertyControlOrSection); diff --git a/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx b/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx index 474f503b30a..99dc3bfbe52 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx @@ -47,6 +47,7 @@ export const apiPluginHasUrl = ( if (pluginType !== PluginType.API) { return false; } + return ( !datasource || !datasource?.datasourceStorages[currentEnvironment]?.datasourceConfiguration @@ -81,11 +82,13 @@ function NewActionButton(props: NewActionButtonProps) { toast.show(ERROR_ADD_API_INVALID_URL(), { kind: "error", }); + return; } if (currentPageId) { setIsSelected(true); + if (datasource) { dispatch( createNewQueryAction( @@ -104,14 +107,19 @@ function NewActionButton(props: NewActionButtonProps) { const handleOnInteraction = useCallback( (open: boolean) => { if (disabled || isLoading) return; + if (!open) { setIsPageSelectionOpen(false); + return; } + if (pages.length === 1) { createQueryAction(currentPageId); + return; } + setIsPageSelectionOpen(true); }, [pages, createQueryAction, disabled, isLoading], @@ -180,6 +188,7 @@ function NewActionButton(props: NewActionButtonProps) { i === 0 ? : null, ]; } + return null; })} diff --git a/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx index d58092f5aee..4c0cfad1f82 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx @@ -128,15 +128,19 @@ class DatasourceRestAPIEditor extends React.Component { // eslint-disable-next-line @typescript-eslint/no-explicit-any isDirty(prop: any) { const { formMeta } = this.props; + return _.get(formMeta, prop + ".visited", false); } ensureAPIKeyDefaultsAreCorrect = () => { if (!this.props.formData) return; + const { authentication } = this.props.formData; + if (!authentication || !_.get(authentication, "addTo")) { this.props.change("authentication.addTo", ApiKeyAuthType.Header); } + if (!authentication || !_.get(authentication, "headerPrefix")) { this.props.change("authentication.headerPefix", "ApiKeyAuthType.Header"); } @@ -144,6 +148,7 @@ class DatasourceRestAPIEditor extends React.Component { ensureOAuthDefaultsAreCorrect = () => { if (!this.props.formData) return; + const { authentication } = this.props.formData; if (!authentication || !_.get(authentication, "grantType")) { @@ -152,9 +157,11 @@ class DatasourceRestAPIEditor extends React.Component { GrantType.ClientCredentials, ); } + if (_.get(authentication, "isTokenHeader") === undefined) { this.props.change("authentication.isTokenHeader", true); } + if ( !this.isDirty("authentication.headerPrefix") && _.get(authentication, "headerPrefix") === undefined @@ -204,7 +211,9 @@ class DatasourceRestAPIEditor extends React.Component { isFeatureEnabled, datasource?.userPermissions || [], ); + if (!formData) return true; + return !formData.url || (!createMode && !canManageDatasource); }; @@ -247,6 +256,7 @@ class DatasourceRestAPIEditor extends React.Component { urlValidator = (value: string) => { const validationRegex = "^(http|https)://"; + if (value) { const regex = new RegExp(validationRegex); @@ -276,6 +286,7 @@ class DatasourceRestAPIEditor extends React.Component { renderEditor = () => { const { formData, messages } = this.props; + if (!formData) return; return ( @@ -399,6 +410,7 @@ class DatasourceRestAPIEditor extends React.Component { renderSelfSignedCertificateFields = () => { const { connection } = this.props.formData; + if (connection?.ssl.authTypeControl) { return (
@@ -418,6 +430,7 @@ class DatasourceRestAPIEditor extends React.Component { const { authType } = this.props.formData; let content; + if (authType === AuthType.OAuth2) { content = this.renderOauth2(); } else if (authType === AuthType.basic) { @@ -427,6 +440,7 @@ class DatasourceRestAPIEditor extends React.Component { } else if (authType === AuthType.bearerToken) { content = this.renderBearerToken(); } + if (content) { return content; } @@ -434,6 +448,7 @@ class DatasourceRestAPIEditor extends React.Component { renderApiKey = () => { const { authentication } = this.props.formData; + return ( <> @@ -541,8 +556,11 @@ class DatasourceRestAPIEditor extends React.Component { | ClientCredentials | AuthorizationCode | undefined; + if (!authentication) return; + let content; + switch (authentication.grantType) { case GrantType.AuthorizationCode: content = this.renderOauth2AuthorizationCode(); @@ -580,6 +598,7 @@ class DatasourceRestAPIEditor extends React.Component { renderOauth2Common = () => { const { formData } = this.props; + return ( <> { const redirectURL = window.location.origin + "/api/v1/datasources/authorize"; + return ( <> {this.renderOauth2Common()} @@ -941,6 +961,7 @@ class DatasourceRestAPIEditor extends React.Component { formName: this.props.formName, initialValue: initialValue, }; + return ( { formName: this.props.formName, isRequired: isRequired, }; + return ( { formName: this.props.formName, isRequired: isRequired, }; + return ( { if (this.props.isDatasourceBeingSaved) { this.closeDialogAndUnblockRoutes(); } + this.setViewModeFromQueryParams(); if ( @@ -315,12 +316,14 @@ class DatasourceEditorRouter extends React.Component { (this.props.datasource as Datasource).datasourceStorages, )[0]; } + return this.state.filterParams.id; }; componentDidMount() { const urlObject = new URL(window.location.href); const pluginId = urlObject?.searchParams.get("pluginId"); + // Create Temp Datasource on component mount, // if user hasnt saved datasource for the first time and refreshed the page if ( @@ -331,6 +334,7 @@ class DatasourceEditorRouter extends React.Component { pluginId, }); } + if (!this.props.viewMode && !!this.props.pluginId) { this.blockRoutes(); } @@ -361,14 +365,17 @@ class DatasourceEditorRouter extends React.Component { const search = new URLSearchParams(this.props.location.search); const responseStatus = search.get("response_status"); const responseMessage = search.get("display_message"); + if (responseStatus) { // Set default error message let message = REST_API_AUTHORIZATION_FAILED; + if (responseStatus === "success") { message = REST_API_AUTHORIZATION_SUCCESSFUL; } else if (responseStatus === "appsmith_error") { message = REST_API_AUTHORIZATION_APPSMITH_ERROR; } + toast.show(responseMessage || createMessage(message)); } } @@ -378,6 +385,7 @@ class DatasourceEditorRouter extends React.Component { // from outside the datasource route setViewModeFromQueryParams() { const params = getQueryParams(); + if (this.props.viewMode) { if ( (params.viewMode === "false" && !this.state.readUrlParams) || @@ -402,9 +410,12 @@ class DatasourceEditorRouter extends React.Component { const { configProperty, controlType, isRequired } = config; const configDetails = this.state.configDetails; const requiredFields = this.state.requiredFields; + if (!configProperty || !configProperty.includes(this.getEnvironmentId())) return; + configDetails[configProperty] = controlType; + if (isRequired) requiredFields[configProperty] = config; // if the required fields being rendered has been hidden, then remove them. @@ -503,6 +514,7 @@ class DatasourceEditorRouter extends React.Component { const prevPath = this.props.history.location.pathname + this.props.history.location.search; + // On reload, it goes from same path to same path, we do not need to show popup in that case if (nextPath !== prevPath && this.props.isFormDirty) { this.setState( @@ -515,6 +527,7 @@ class DatasourceEditorRouter extends React.Component { }, this.routesBlockFormChangeCallback.bind(this), ); + return false; } }), @@ -544,6 +557,7 @@ class DatasourceEditorRouter extends React.Component { onDiscard() { this.props.resetForm(this.props.formName); this.closeDialogAndUnblockRoutes(); + if (this.state.switchFilterBlocked) { //unblock switch filter this.setState({ switchFilterBlocked: false }); @@ -552,6 +566,7 @@ class DatasourceEditorRouter extends React.Component { this.props.deleteTempDSFromDraft(); this.props.datasourceDiscardAction(this.props?.pluginId); } + this.state.navigation(); this.props.datasourceDiscardAction(this.props?.pluginId); @@ -573,6 +588,7 @@ class DatasourceEditorRouter extends React.Component { this.props.toggleSaveActionFlag(false); this.props.toggleSaveActionFromPopupFlag(false); this.setState({ routesBlocked: false }); + if (isNavigateBack) { this.state.navigation(); } @@ -596,6 +612,7 @@ class DatasourceEditorRouter extends React.Component { if (!this.props.viewMode) { this.props.setCurrentEditingEnvironmentID(id); } + if (this.state.filterParams.id !== id) { if ( !isEmpty(this.props.formData) && @@ -609,10 +626,12 @@ class DatasourceEditorRouter extends React.Component { this.updateFilterSuccess(id, name, userPermissions); }, }); + return false; } else { this.props.resetForm(this.props.formName); } + return this.updateFilterSuccess(id, name, userPermissions); } else if ( !isStorageEnvironmentCreated(this.props.formData as Datasource, id) @@ -620,6 +639,7 @@ class DatasourceEditorRouter extends React.Component { return this.updateFilterSuccess(id, name, userPermissions); } } + return true; }; @@ -638,6 +658,7 @@ class DatasourceEditorRouter extends React.Component { ) { acc[envId] = datasourceStorages[envId]; } + return acc; }, {}, @@ -645,6 +666,7 @@ class DatasourceEditorRouter extends React.Component { const initialValues = merge(getConfigInitialValues(this.props.formConfig), { properties: [], }); + if (!datasourceStoragesWithId.hasOwnProperty(id)) { // Create the new datasource storage object const newDsStorageObject: DatasourceStorage = { @@ -678,6 +700,7 @@ class DatasourceEditorRouter extends React.Component { }, }); } + AnalyticsUtil.logEvent("SWITCH_ENVIRONMENT", { fromEnvId: this.state.filterParams.id, toEnvId: id, @@ -696,6 +719,7 @@ class DatasourceEditorRouter extends React.Component { requiredFields: {}, }); this.blockRoutes(); + return true; }; @@ -746,6 +770,7 @@ class DatasourceEditorRouter extends React.Component { (datasource as Datasource).name, this.state.filterParams.name, ); + if (toastMessage.message) return ( @@ -763,6 +788,7 @@ class DatasourceEditorRouter extends React.Component { ); + return null; } @@ -771,6 +797,7 @@ class DatasourceEditorRouter extends React.Component { this.props; const shouldViewMode = viewMode && !isInsideReconnectModal; + // Check for specific form types first return ( pluginDatasourceForm === DatasourceComponentTypes.RestAPIDatasourceForm && @@ -869,6 +896,7 @@ class DatasourceEditorRouter extends React.Component { renderViewConfigChild = () => { const { datasource, formConfig, viewMode } = this.props; + return ( {!isNil(formConfig) && !isNil(datasource) ? ( @@ -884,11 +912,13 @@ class DatasourceEditorRouter extends React.Component { shouldShowTabs = () => { const { isPluginAllowedToPreviewData } = this.props; + return isPluginAllowedToPreviewData; }; renderTabsForViewMode = () => { const { datasource } = this.props; + return this.shouldShowTabs() ? ( { /> ); } + history.push( saasEditorDatasourceIdURL({ basePageId, @@ -953,6 +984,7 @@ class DatasourceEditorRouter extends React.Component { datasourceId, }), ); + return null; } @@ -1051,16 +1083,20 @@ class DatasourceEditorRouter extends React.Component { formData, pluginDatasourceForm, } = this.props; + if ( pluginDatasourceForm === DatasourceComponentTypes.RestAPIDatasourceForm ) { const createMode = datasourceId === TEMP_DATASOURCE_ID; + if (!formData) return true; + return ( !(formData as ApiDatasourceForm).url || (!createMode && !canManageDatasource) ); } + return validate( this.state.requiredFields, formData, diff --git a/app/client/src/pages/Editor/DatasourceInfo/DatasorceTabs.tsx b/app/client/src/pages/Editor/DatasourceInfo/DatasorceTabs.tsx index d89d837e27f..3960d00402a 100644 --- a/app/client/src/pages/Editor/DatasourceInfo/DatasorceTabs.tsx +++ b/app/client/src/pages/Editor/DatasourceInfo/DatasorceTabs.tsx @@ -71,6 +71,7 @@ const DatasourceTabs = (props: DatasourceTabProps) => { currentEnvironmentId, ) : false; + return ( { let url; + if (props.plugin && props.plugin.type === PluginType.SAAS) { url = saasEditorDatasourceIdURL({ basePageId: entityId, @@ -86,6 +87,7 @@ const ExplorerDatasourceEntity = React.memo( ); let isDefaultExpanded = false; + if (expandDatasourceId === props.datasource.id) { isDefaultExpanded = true; } else if (queryAction && isStoredDatasource(queryAction.datasource)) { diff --git a/app/client/src/pages/Editor/DatasourceInfo/DatasourceStructure.tsx b/app/client/src/pages/Editor/DatasourceInfo/DatasourceStructure.tsx index e5ffc892e23..bccdbe2256b 100644 --- a/app/client/src/pages/Editor/DatasourceInfo/DatasourceStructure.tsx +++ b/app/client/src/pages/Editor/DatasourceInfo/DatasourceStructure.tsx @@ -51,6 +51,7 @@ const DatasourceStructureItem = memo((props: DatasourceStructureItemProps) => { const dbStructure = props.dbStructure; let templateMenu = null; const [active, setActive] = useState(false); + useCloseMenuOnScroll(SIDEBAR_ID, active, () => setActive(false)); const collapseRef = useRef(null); diff --git a/app/client/src/pages/Editor/DatasourceInfo/DatasourceStructureNotFound.tsx b/app/client/src/pages/Editor/DatasourceInfo/DatasourceStructureNotFound.tsx index 80e51e02c8c..35512803cf1 100644 --- a/app/client/src/pages/Editor/DatasourceInfo/DatasourceStructureNotFound.tsx +++ b/app/client/src/pages/Editor/DatasourceInfo/DatasourceStructureNotFound.tsx @@ -57,6 +57,7 @@ const DatasourceStructureNotFound = (props: Props) => { if (props.context === DatasourceStructureContext.DATASOURCE_VIEW_MODE) { props?.customEditDatasourceFn && props?.customEditDatasourceFn(); + return; } @@ -66,6 +67,7 @@ const DatasourceStructureNotFound = (props: Props) => { params: { ...omit(getQueryParams(), "viewMode"), viewMode: false }, generateEditorPath: true, }); + history.push(url); }; diff --git a/app/client/src/pages/Editor/DatasourceInfo/GoogleSheetSchema.tsx b/app/client/src/pages/Editor/DatasourceInfo/GoogleSheetSchema.tsx index 8eb2e5b0198..53c06181a4f 100644 --- a/app/client/src/pages/Editor/DatasourceInfo/GoogleSheetSchema.tsx +++ b/app/client/src/pages/Editor/DatasourceInfo/GoogleSheetSchema.tsx @@ -130,6 +130,7 @@ function GoogleSheetSchema(props: Props) { try { const element = document.querySelector(elementId); const container = document.querySelector(containerId); + if (element && container) { const elementRect = element.getBoundingClientRect(); const containerRect = container.getBoundingClientRect(); @@ -157,6 +158,7 @@ function GoogleSheetSchema(props: Props) { ), ); } + if (!isEmpty(spreadSheet) && collapseSpreadsheet) { dispatch( setEntityCollapsibleState(`${datasourceId}-${spreadSheet}`, false), @@ -232,8 +234,10 @@ function GoogleSheetSchema(props: Props) { setSelectedSpreadsheet((ss) => { setSelectedSheet((s) => { collapseAccordions(datasource?.id || "", ss.value, s.value); + return {}; }); + return {}; }); } diff --git a/app/client/src/pages/Editor/DatasourceInfo/HideGeneratePageButton.test.tsx b/app/client/src/pages/Editor/DatasourceInfo/HideGeneratePageButton.test.tsx index 0970f123ca4..9a6cb4c8372 100644 --- a/app/client/src/pages/Editor/DatasourceInfo/HideGeneratePageButton.test.tsx +++ b/app/client/src/pages/Editor/DatasourceInfo/HideGeneratePageButton.test.tsx @@ -99,6 +99,7 @@ describe("DatasourceViewModeSchema Component", () => { if (selector === getNumberOfEntitiesInCurrentPage) { return 0; } + return selector(baseStoreForSpec); // Default case for other selectors }); renderBaseDatasourceComponent(); @@ -107,6 +108,7 @@ describe("DatasourceViewModeSchema Component", () => { const generatePageButton = screen.queryByText( createMessage(DATASOURCE_GENERATE_PAGE_BUTTON), ); + expect(generatePageButton).not.toBeInTheDocument(); }); @@ -134,6 +136,7 @@ describe("DatasourceViewModeSchema Component", () => { // Check that the "New Query" button is rendered as primary const newQuerySpan = screen.getByText(getCreateButtonText(PluginType.DB)); const newQueryButton = newQuerySpan.closest("button"); + expect(newQueryButton).toHaveAttribute("kind", "primary"); }); }); @@ -148,6 +151,7 @@ describe("GoogleSheetSchema Component", () => { if (selector === getNumberOfEntitiesInCurrentPage) { return 0; } + return selector(baseStoreForSpec); // Default case for other selectors }); renderGoogleSheetDSComponent(); @@ -156,6 +160,7 @@ describe("GoogleSheetSchema Component", () => { const generatePageButton = screen.queryByText( createMessage(DATASOURCE_GENERATE_PAGE_BUTTON), ); + expect(generatePageButton).not.toBeInTheDocument(); }); }); @@ -186,6 +191,7 @@ describe("DSFormHeader Component", () => { const generatePageButton = screen.queryByText( createMessage(DATASOURCE_GENERATE_PAGE_BUTTON), ); + expect(generatePageButton).not.toBeInTheDocument(); }); }); diff --git a/app/client/src/pages/Editor/DatasourceInfo/QueryTemplates.tsx b/app/client/src/pages/Editor/DatasourceInfo/QueryTemplates.tsx index 15e2c13a9a5..71c1ad6c1e7 100644 --- a/app/client/src/pages/Editor/DatasourceInfo/QueryTemplates.tsx +++ b/app/client/src/pages/Editor/DatasourceInfo/QueryTemplates.tsx @@ -204,6 +204,7 @@ export function QueryTemplates(props: QueryTemplatesProps) { } else { createQueryAction(template); } + props.onSelect(); }} > diff --git a/app/client/src/pages/Editor/EditorName/NavigationMenu.tsx b/app/client/src/pages/Editor/EditorName/NavigationMenu.tsx index f30f69b801d..8edf49d58ba 100644 --- a/app/client/src/pages/Editor/EditorName/NavigationMenu.tsx +++ b/app/client/src/pages/Editor/EditorName/NavigationMenu.tsx @@ -13,6 +13,7 @@ interface NavigationMenuProps { export function NavigationMenu(props: NavigationMenuProps) { const { menuItems, setIsPopoverOpen } = props; + return ( {menuItems?.map((item, idx) => { diff --git a/app/client/src/pages/Editor/EditorName/NavigationMenuItem.tsx b/app/client/src/pages/Editor/EditorName/NavigationMenuItem.tsx index d0d0caf2e6c..d75f9e81a4c 100644 --- a/app/client/src/pages/Editor/EditorName/NavigationMenuItem.tsx +++ b/app/client/src/pages/Editor/EditorName/NavigationMenuItem.tsx @@ -50,7 +50,9 @@ export function NavigationMenuItem({ const handleClick = (e: React.SyntheticEvent, item: MenuItemData) => { setIsPopoverOpen(false); + if (item.onClick) item.onClick(e); + AnalyticsUtil.logEvent("APP_MENU_OPTION_CLICK", { option: item.text, }); diff --git a/app/client/src/pages/Editor/EditorName/index.tsx b/app/client/src/pages/Editor/EditorName/index.tsx index abb3fd5743d..fbc50ab910d 100644 --- a/app/client/src/pages/Editor/EditorName/index.tsx +++ b/app/client/src/pages/Editor/EditorName/index.tsx @@ -65,6 +65,7 @@ export function EditorName(props: EditorNameProps) { const onBlur = (value: string) => { if (props.onBlur) props.onBlur(value); + setIsEditingDefault(false); }; @@ -74,6 +75,7 @@ export function EditorName(props: EditorNameProps) { kind: "error", }); } + return false; }; @@ -81,6 +83,7 @@ export function EditorName(props: EditorNameProps) { (e: React.MouseEvent) => { setIsEditing(true); const errorMessage = inputValidation && inputValidation(defaultValue); + setIsInvalid(errorMessage ? errorMessage : false); e.preventDefault(); e.stopPropagation(); diff --git a/app/client/src/pages/Editor/EditorName/useNavigationMenuData.ts b/app/client/src/pages/Editor/EditorName/useNavigationMenuData.ts index c1001c55014..4d2b554987c 100644 --- a/app/client/src/pages/Editor/EditorName/useNavigationMenuData.ts +++ b/app/client/src/pages/Editor/EditorName/useNavigationMenuData.ts @@ -67,17 +67,21 @@ export const useNavigationMenuData = ({ const exportAppAsJSON = useCallback(() => { const id = `t--export-app-link`; const existingLink = document.getElementById(id); + existingLink && existingLink.remove(); const link = document.createElement("a"); const branchName = currentApplication?.gitApplicationMetadata?.branchName; + link.href = getExportAppAPIRoute(applicationId, branchName); link.id = id; document.body.appendChild(link); + // @ts-expect-error: Types are not available if (!window.Cypress) { link.click(); } + toast.show(`Successfully exported ${currentApplication?.name}`, { kind: "success", }); diff --git a/app/client/src/pages/Editor/EditorSaveIndicator.tsx b/app/client/src/pages/Editor/EditorSaveIndicator.tsx index f4423695459..18ef82f72b9 100644 --- a/app/client/src/pages/Editor/EditorSaveIndicator.tsx +++ b/app/client/src/pages/Editor/EditorSaveIndicator.tsx @@ -19,6 +19,7 @@ export function EditorSaveIndicator({ }) { let saveStatusIcon: React.ReactNode; let saveStatusText = ""; + if (isSaving) { saveStatusIcon = ; saveStatusText = createMessage(EDITOR_HEADER.saving); diff --git a/app/client/src/pages/Editor/EditorShareButton.tsx b/app/client/src/pages/Editor/EditorShareButton.tsx index f0b38106ec9..25807d6b7d2 100644 --- a/app/client/src/pages/Editor/EditorShareButton.tsx +++ b/app/client/src/pages/Editor/EditorShareButton.tsx @@ -20,6 +20,7 @@ export const EditorShareButton = ({ const filteredSharedUserList = sharedUserList.filter( (user) => user.username !== currentUser?.username, ); + return ( { const matchedSections: IMatchedSection[] = []; + function _getSectionId( config: readonly PropertyPaneConfig[], rootSectionId?: string, ) { for (let index = 0; index < config.length; index++) { const sectionChildren = config[index].children; + if (sectionChildren) { for ( let childIndex = 0; @@ -28,6 +30,7 @@ export const getSectionId = ( const controlConfig = sectionChildren[ childIndex ] as PropertyPaneControlConfig; + if ( controlConfig.propertyName && matchesPropertyPath(propertyPath, controlConfig.propertyName) @@ -101,6 +104,7 @@ export const getSelectedTabIndex = ( ): number | undefined { for (let index = 0; index < config.length; index++) { const sectionChildren = config[index].children; + if (sectionChildren) { for ( let childIndex = 0; @@ -110,6 +114,7 @@ export const getSelectedTabIndex = ( const controlConfig = sectionChildren[ childIndex ] as PropertyPaneControlConfig; + if ( matchesPropertyPath( propertyPath, @@ -120,6 +125,7 @@ export const getSelectedTabIndex = ( return 1; } else if (controlConfig.children) { const index = _getSelectedTabIndex(controlConfig.children); + // We want to continue searching if there isn't a match, so // we don't return/exit unless there is a match if (index) return index; @@ -138,6 +144,7 @@ export const getSelectedTabIndex = ( } const finalIndex = _getSelectedTabIndex(config); + return finalIndex ?? 0; }; @@ -157,8 +164,10 @@ export const getPropertyPanePanelNavigationConfig = ( parentPanelPath = "", ) { let stack: IPanelStack[] = []; + for (let index = 0; index < config.length; index++) { const sectionChildren = config[index].children; + if (sectionChildren) { for ( let childIndex = 0; @@ -171,13 +180,16 @@ export const getPropertyPanePanelNavigationConfig = ( const currentProperty = controlConfig.propertyName; let pathList = propertyPath.split("."); + if (panelDepth !== 0) { const pathWithoutRootProperty = propertyPath.replace( `${rootProperty}.`, "", ); + pathList = [rootProperty, ...pathWithoutRootProperty.split(".")]; } + if ( matchesPropertyPath(propertyPath, currentProperty, "start") && controlConfig.hasOwnProperty("panelConfig") @@ -187,6 +199,7 @@ export const getPropertyPanePanelNavigationConfig = ( `${currentProperty}.`, "", ); + pathList = [ currentProperty, ...pathWithoutRootProperty.split("."), @@ -240,6 +253,7 @@ export const getPropertyPanePanelNavigationConfig = ( }); // When we don't have multiple tabs we just have `children` const panelConfig = contentChildren; + stack = stack.concat( _getNavigationConfig( panelConfig as readonly PropertyPaneConfig[], @@ -248,15 +262,18 @@ export const getPropertyPanePanelNavigationConfig = ( newParentPanelPath, ), ); + return stack; } } } } + return stack; } const finalConfig = _getNavigationConfig(config, 0); + return finalConfig; }; @@ -286,10 +303,12 @@ function getPanelIndex( panelDepth: number, ) { const obj = get(widgetProps, panelTabPath); + // The index field never seems to change for the widget if (widgetProps.type === "TABLE_WIDGET_V2" && panelDepth === 0) { const column = panelTabPath.split(".")[1]; const columnOrder: string[] = get(widgetProps, "columnOrder"); + return columnOrder.indexOf(column); } @@ -307,6 +326,7 @@ function getNextParentPropertPath( ); const index = remainingPath.split(".")[0]; const originalCurrentProperty = remainingPath.split(".")[1]; + if (!prevParentPropertyPath) { return `${currentProperty}.${index}`; } diff --git a/app/client/src/pages/Editor/EntityNavigation/factory.ts b/app/client/src/pages/Editor/EntityNavigation/factory.ts index 66cfdde6d84..5ad5efe48da 100644 --- a/app/client/src/pages/Editor/EntityNavigation/factory.ts +++ b/app/client/src/pages/Editor/EntityNavigation/factory.ts @@ -16,6 +16,7 @@ export default class EntityNavigationFactory { ActionPaneNavigation.create, entityInfo, ); + return instance; case ENTITY_TYPE.JSACTION: return new JSObjectsPaneNavigation(entityInfo); diff --git a/app/client/src/pages/Editor/EntityNotFoundPane.tsx b/app/client/src/pages/Editor/EntityNotFoundPane.tsx index 0884ed068ce..afe7133dfd8 100644 --- a/app/client/src/pages/Editor/EntityNotFoundPane.tsx +++ b/app/client/src/pages/Editor/EntityNotFoundPane.tsx @@ -57,6 +57,7 @@ interface Props { function EntityNotFoundPane(props: Props) { const history = useHistory(); + return ( { pluginType={action.pluginType} /> ); + return ( = [ // eslint-disable-next-line @typescript-eslint/no-explicit-any getIcon: (action: any, plugin: Plugin, remoteIcon?: boolean) => { const isGraphql = isGraphqlPlugin(plugin); + if ( plugin && plugin.type === PluginType.API && @@ -113,8 +114,10 @@ export const ACTION_PLUGIN_MAP: Array = [ !isGraphql ) { const method = action?.actionConfiguration?.httpMethod; + if (method) return ApiMethodIcon(method); } + if (plugin && plugin.iconLocation) return ( { option.onSelect?.(option); }, 0); + if (option.value === "delete" && !option.confirmDelete) { handleOpenChange(true); } else { @@ -115,6 +116,7 @@ export default function TreeDropdown(props: TreeDropdownProps) { ); } + const list = optionTree.map(renderTreeOption); const menuItems = ( { isFeatureEnabled, datasourcePermissions, ); + return ( ` overflow: hidden; `; + export function EntityCollapse(props: { children: ReactNode; isOpen: boolean; @@ -14,6 +15,7 @@ export function EntityCollapse(props: { collapseRef?: RefObject | null; }) { if (!props.children) return null; + return ( { document.addEventListener("click", handleOutsideClick); + return () => document.removeEventListener("click", handleOutsideClick); }, [show]); @@ -62,6 +63,7 @@ export function EntityProperties() { if (selectedWidgetId && show) { const canvasWidgets = store.getState().entities.canvasWidgets; const selectedWidget = canvasWidgets[selectedWidgetId]; + if (selectedWidget) dispatch({ type: ReduxActionTypes.SET_ENTITY_INFO, @@ -94,21 +96,25 @@ export function EntityProperties() { "entity-properties-container", ) as HTMLElement; const paths = e.composedPath(); + if (!paths?.includes(entityPropertiesContainer)) closeContainer(e); }; useEffect(() => { const element = document.getElementById(`entity-${entityId}`); const rect = element?.getBoundingClientRect(); + if (ref.current && rect) { const top = rect?.top; let bottom; + if ( top + BindingContainerMaxHeight > window.innerHeight - BOTTOM_BAR_HEIGHT ) { bottom = window.innerHeight - rect?.bottom - EntityHeight; } + if (bottom) { ref.current.style.bottom = bottom + "px"; ref.current.style.top = "unset"; @@ -116,6 +122,7 @@ export function EntityProperties() { ref.current.style.top = top - EntityHeight + "px"; ref.current.style.bottom = "unset"; } + ref.current.style.left = DEFAULT_EXPLORER_PANE_WIDTH + "px"; } }, [entityId]); diff --git a/app/client/src/pages/Editor/Explorer/Entity/Name.tsx b/app/client/src/pages/Editor/Explorer/Entity/Name.tsx index 80d53a990c7..de937dd86a1 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/Name.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/Name.tsx @@ -57,8 +57,10 @@ export const replace = ( keyIndex = 1, ): JSX.Element[] => { const occurrenceIndex = str.indexOf(delimiter); + if (occurrenceIndex === -1) return [{str}]; + const sliced = str.slice(occurrenceIndex + delimiter.length); const nextOccurenceIndex = sliced.indexOf(delimiter); const rest = str.slice( @@ -74,6 +76,7 @@ export const replace = ( {token} , ].concat(replace(rest, delimiter, className, keyIndex + 1)); + return final; }; @@ -105,6 +108,7 @@ export const EntityName = React.memo( // Check to show tooltip on hover const nameWrapperRef = useRef(null); const [showTooltip, setShowTooltip] = useState(false); + useEffect(() => { setShowTooltip(!!isEllipsisActive(nameWrapperRef.current)); }, [updatedName, name]); @@ -123,8 +127,10 @@ export const EntityName = React.memo( searchTokenizationDelimiter, searchHighlightSpanClassName, ); + return final; } + return updatedName; }, [searchKeyword, updatedName]); diff --git a/app/client/src/pages/Editor/Explorer/Entity/index.tsx b/app/client/src/pages/Editor/Explorer/Entity/index.tsx index 673854b9fa4..55310bb4f22 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/index.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/index.tsx @@ -302,6 +302,7 @@ export const Entity = forwardRef( props.onToggle && props.onToggle(!isOpen); // Make sure this entity is enabled before toggling the collpse of children. !props.disabled && open(!isOpen); + if (props.runActionOnExpand && !isOpen) { props.action && props.action(e); } @@ -331,6 +332,7 @@ export const Entity = forwardRef( const enterEditMode = useCallback(() => { if (!canEditEntityName) return; + props.updateEntityName && dispatch({ type: ReduxActionTypes.INIT_EXPLORER_ENTITY_NAME_EDIT, @@ -341,6 +343,7 @@ export const Entity = forwardRef( }, [dispatch, props.entityId, props.updateEntityName]); const itemRef = useRef(null); + useClick(itemRef, handleClick, noop); const addButton = props.customAddButton || ( diff --git a/app/client/src/pages/Editor/Explorer/EntityExplorer.test.tsx b/app/client/src/pages/Editor/Explorer/EntityExplorer.test.tsx index 4fa6867b745..f5a753df35b 100644 --- a/app/client/src/pages/Editor/Explorer/EntityExplorer.test.tsx +++ b/app/client/src/pages/Editor/Explorer/EntityExplorer.test.tsx @@ -17,6 +17,7 @@ import WidgetsEditorEntityExplorer from "../WidgetsEditorEntityExplorer"; jest.useFakeTimers(); const pushState = jest.spyOn(window.history, "pushState"); + // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any pushState.mockImplementation((state: any, title: any, url: any) => { @@ -90,11 +91,13 @@ describe("Entity Explorer tests", () => { const widgetsTree: any = component.queryByText("Widgets", { selector: "div.t--entity-name", }); + act(() => { fireEvent.click(widgetsTree); jest.runAllTimers(); }); const tabsWidget = component.queryByText(children[0].widgetName); + expect(tabsWidget).toBeTruthy(); }); @@ -103,6 +106,7 @@ describe("Entity Explorer tests", () => { widgetSelectionsActions, "selectWidgetInitAction", ); + beforeEach(() => { spyWidgetSelection.mockClear(); }); @@ -126,6 +130,7 @@ describe("Entity Explorer tests", () => { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any const tabsWidget: any = component.queryByText(children[0].widgetName); + act(() => { fireEvent.click(tabsWidget); jest.runAllTimers(); @@ -163,6 +168,7 @@ describe("Entity Explorer tests", () => { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any const checkBox: any = component.queryByText(children[0].widgetName); + act(() => { fireEvent.click(checkBox); jest.runAllTimers(); @@ -170,6 +176,7 @@ describe("Entity Explorer tests", () => { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any const switchWidget: any = component.queryByText(children[1].widgetName); + expect(spyWidgetSelection).toHaveBeenCalledWith( SelectionRequestType.One, ["checkboxWidgetId"], @@ -332,6 +339,7 @@ describe("Entity Explorer tests", () => { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any const buttonWidget: any = component.queryByText(children[2].widgetName); + act(() => { fireEvent.click(buttonWidget, { shiftKey: true, @@ -349,6 +357,7 @@ describe("Entity Explorer tests", () => { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any const checkBoxWidget: any = component.queryByText(children[0].widgetName); + act(() => { fireEvent.click(checkBoxWidget, { shiftKey: true, @@ -366,6 +375,7 @@ describe("Entity Explorer tests", () => { const chartWidget: any = component.queryByText( containerChildren[1].widgetName, ); + act(() => { fireEvent.click(chartWidget, { shiftKey: true, diff --git a/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx b/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx index 11feb000b46..c86c3c29305 100644 --- a/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx +++ b/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx @@ -74,6 +74,7 @@ function EntityExplorer({ isActive }: { isActive: boolean }) { AnalyticsUtil.logEvent("EXPLORER_WIDGET_CLICK"); history.push(builderURL({ basePageId })); dispatch(forceOpenWidgetPanel(true)); + if (isFirstTimeUserOnboardingEnabled) { dispatch(toggleInOnboardingWidgetSelection(true)); } diff --git a/app/client/src/pages/Editor/Explorer/ExplorerIcons.tsx b/app/client/src/pages/Editor/Explorer/ExplorerIcons.tsx index e9bbb6d5275..8aa0204af4a 100644 --- a/app/client/src/pages/Editor/Explorer/ExplorerIcons.tsx +++ b/app/client/src/pages/Editor/Explorer/ExplorerIcons.tsx @@ -37,26 +37,31 @@ export const defaultPageIcon = ( export const hiddenPageIcon = ; const WidgetIcon = MenuIcons.WIDGETS_ICON; + export const widgetIcon = ( ); const DBQueryIcon = MenuIcons.DATASOURCE_ICON_v2; + export const dbQueryIcon = ( ); const JSIcon = MenuIcons.JS_ICON_V2; + export const jsIcon = ( ); const JSFileIcon = MenuIcons.JS_FILE_ICON; + export const jsFileIcon = ( ); const JSFunctionIcon = MenuIcons.JS_FUNCTION_ICON; + export const jsFunctionIcon = ( { /> ); } + return ; }; @@ -140,6 +148,7 @@ export function MethodTag(props: { type: keyof typeof HTTP_METHOD }) { } const CurrentPageIcon = MenuIcons.CURRENT_PAGE_ICON; + export const currentPageIcon = ( { if (!activeActionBaseId) return; + document.getElementById(`entity-${activeActionBaseId}`)?.scrollIntoView({ block: "nearest", inline: "nearest", @@ -149,6 +151,7 @@ function Files() { // eslint-disable-next-line @typescript-eslint/no-explicit-any (item: any) => { if (item.kind === SEARCH_ITEM_TYPES.sectionTitle) return; + if (item.action) { dispatch( item.action( diff --git a/app/client/src/pages/Editor/Explorer/JSActions/JSActionEntity.tsx b/app/client/src/pages/Editor/Explorer/JSActions/JSActionEntity.tsx index eb949fc2fcd..e1c1c20e2b4 100644 --- a/app/client/src/pages/Editor/Explorer/JSActions/JSActionEntity.tsx +++ b/app/client/src/pages/Editor/Explorer/JSActions/JSActionEntity.tsx @@ -92,6 +92,7 @@ export const ExplorerJSCollectionEntity = memo( name={jsAction.name} /> ); + return ( ` function isValidJSFileURL(url: string) { const JS_FILE_REGEX = /(?:https?):\/\/(\w+:?\w*)?(\S+)(:\d+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/; + return JS_FILE_REGEX.test(url); } @@ -177,6 +178,7 @@ function StatusIcon(props: { () => (action ? { onClick: action } : {}), [action], ); + if (status === InstallState.Success || isInstalled) return ( @@ -188,13 +190,16 @@ function StatusIcon(props: { /> ); + if (status === InstallState.Failed) return ( ); + if (status === InstallState.Queued) return ; + return (