diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2d48262987..b5cf66b40a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: - uses: ./.github/actions/git-creds - uses: ./.github/actions/download-builds - name: Generate new versions - run: yarn lerna version --exact --conventional-commits -y + run: yarn lerna version --exact --conventional-commits --conventional-graduate -y - run: yarn config set registry https://registry.npmjs.org/ - name: Setup .npmrc for publish id: setup-npmrc diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 93b39bc347..8a2d89ef31 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,8 +40,7 @@ jobs: SINCE_FLAG: ${{ steps.determine-since-flag.outputs.since_flag }} run: yarn lerna run ${{ matrix.command }} $SINCE_FLAG - tests_e2e: - name: Run end-to-end tests + e2e: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -56,7 +55,7 @@ jobs: - uses: ./.github/actions/download-builds - name: Install playwright browsers run: npx playwright install --with-deps - - name: Run tests + - name: Run e2e tests run: yarn lerna run test:e2e $SINCE_FLAG --scope "@vibe/testkit" - uses: actions/upload-artifact@v4 if: ${{ always() }} @@ -64,4 +63,4 @@ jobs: name: test-results path: | packages/testkit/reports - packages/testkit/test-results \ No newline at end of file + packages/testkit/test-results diff --git a/packages/codemod/README.md b/packages/codemod/README.md index 5fddb43000..edc53f782a 100644 --- a/packages/codemod/README.md +++ b/packages/codemod/README.md @@ -66,3 +66,8 @@ The following migrations are included in this CLI: - **Migration Type**: `v3` (`--migration v3`) - **Description**: This migration transforms components and files to comply with version 3 of @vibe/code. + +### `enums` Migration + +- **Migration Type**: `enums` (`--migration enums`) +- **Description**: This migration transforms enums to TS types with version 3 of @vibe/code. diff --git a/packages/codemod/package.json b/packages/codemod/package.json index af3bed79ce..48c23e418a 100644 --- a/packages/codemod/package.json +++ b/packages/codemod/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/codemod", - "version": "0.1.0", + "version": "1.0.0-rc.0", "description": "Vibe's component library migration tool", "repository": { "type": "git", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 299d8a8e09..c952f11968 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,85 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [2.147.1](https://github.com/mondaycom/vibe/compare/monday-ui-react-core@2.147.0...monday-ui-react-core@2.147.1) (2024-11-21) + + +### Bug Fixes + +* **AvatarGroup:** pass dialogContainerSelector from AvatarGroup to AvatarGroupCounter ([#2602](https://github.com/mondaycom/vibe/issues/2602)) ([571e908](https://github.com/mondaycom/vibe/commit/571e908bbc7e8647444026d08a6fe93a2c000330)) +* **TextField:** when inputValue is undefined, length check fails ([#2603](https://github.com/mondaycom/vibe/issues/2603)) ([ad98340](https://github.com/mondaycom/vibe/commit/ad983408dcbdfc725f9106c0a314c7c84ffb66ec)) + + + + + +# [2.147.0](https://github.com/mondaycom/vibe/compare/monday-ui-react-core@2.146.0...monday-ui-react-core@2.147.0) (2024-11-20) + + +### Features + +* **Switcher:** update icon ([#2599](https://github.com/mondaycom/vibe/issues/2599)) ([cdac401](https://github.com/mondaycom/vibe/commit/cdac401ee11c14c2ae3ca2d4fe62aa579b2dd1c0)) + + + + + +# [2.146.0](https://github.com/mondaycom/vibe/compare/monday-ui-react-core@2.145.2...monday-ui-react-core@2.146.0) (2024-11-19) + + +### Bug Fixes + +* **TextArea:** Error state not correctly set when maxlength is exceeded ([#2588](https://github.com/mondaycom/vibe/issues/2588)) ([5a140e8](https://github.com/mondaycom/vibe/commit/5a140e8f335c4b2f4c83ae6beeae3de7db55d746)) + + +### Features + +* **Switcher:** update icon ([#2597](https://github.com/mondaycom/vibe/issues/2597)) ([3c045b1](https://github.com/mondaycom/vibe/commit/3c045b18caf3dad36787c5ee9e84ebb3f71425b2)) + + + + + +## [2.145.2](https://github.com/mondaycom/vibe/compare/monday-ui-react-core@2.145.1...monday-ui-react-core@2.145.2) (2024-11-18) + + +### Bug Fixes + +* **Switcher:** revert icon change to older version ([#2596](https://github.com/mondaycom/vibe/issues/2596)) ([202e5a6](https://github.com/mondaycom/vibe/commit/202e5a6a1e20b3fc9c9e20185d8d46fc713650b6)) + + + + + +## [2.145.1](https://github.com/mondaycom/vibe/compare/monday-ui-react-core@2.145.0...monday-ui-react-core@2.145.1) (2024-11-18) + + +### Bug Fixes + +* **table:** horizontal scroll on react 18 ([#2594](https://github.com/mondaycom/vibe/issues/2594)) ([09c0dc8](https://github.com/mondaycom/vibe/commit/09c0dc83676f5b5a0504e25db0e26227f6b003c9)) + + + + + +# [2.145.0](https://github.com/mondaycom/vibe/compare/monday-ui-react-core@2.144.0...monday-ui-react-core@2.145.0) (2024-11-18) + + +### Bug Fixes + +* **TextArea:** don't show the help section if no help text or char co… ([#2590](https://github.com/mondaycom/vibe/issues/2590)) ([162a77d](https://github.com/mondaycom/vibe/commit/162a77d3c734f7833772e5567222de5698108356)) +* **WhatsNew:** update icon ([#2592](https://github.com/mondaycom/vibe/issues/2592)) ([fbcb99f](https://github.com/mondaycom/vibe/commit/fbcb99f257b624ee7a64eb270c30292b25f6e2ef)) + + +### Features + +* **AvatarGroupCounter:** add option to render MenuButton on a container ([#2591](https://github.com/mondaycom/vibe/issues/2591)) ([d286b28](https://github.com/mondaycom/vibe/commit/d286b285900e73b65fc7658b61fb59af8dc1d846)) +* **PinFull:** new icon ([#2589](https://github.com/mondaycom/vibe/issues/2589)) ([1e34a3c](https://github.com/mondaycom/vibe/commit/1e34a3cff3ee9d1be3d62643258fbca20a2bed83)) + + + + + # [2.144.0](https://github.com/mondaycom/vibe/compare/monday-ui-react-core@2.143.1...monday-ui-react-core@2.144.0) (2024-11-15) diff --git a/packages/core/package.json b/packages/core/package.json index 2a7e981085..999feb17d7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -88,7 +88,7 @@ "classnames": "^2.3.2", "framer-motion": "^6.5.1", "lodash-es": "^4.17.21", - "monday-ui-style": "0.17.0", + "monday-ui-style": "0.20.0", "prop-types": "^15.8.1", "react-dates": "21.8.0", "react-inlinesvg": "^4.1.3", diff --git a/packages/core/src/components/AvatarGroup/AvatarGroup.types.ts b/packages/core/src/components/AvatarGroup/AvatarGroup.types.ts index 28389baf47..9d7688de1d 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroup.types.ts +++ b/packages/core/src/components/AvatarGroup/AvatarGroup.types.ts @@ -7,4 +7,8 @@ export type AvatarGroupCounterVisualProps = { maxDigits?: number; ariaLabelItemsName?: string; noAnimation?: boolean; + /** + * Relevant only for when AvatarGroup contains a clickable avatar + */ + dialogContainerSelector?: string; }; diff --git a/packages/core/src/components/AvatarGroup/AvatarGroupCounter.tsx b/packages/core/src/components/AvatarGroup/AvatarGroupCounter.tsx index 205cb56137..053ff5c28b 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroupCounter.tsx +++ b/packages/core/src/components/AvatarGroup/AvatarGroupCounter.tsx @@ -48,7 +48,8 @@ const AvatarGroupCounter: React.FC = ({ prefix: counterPrefix = "+", maxDigits: counterMaxDigits = 3, ariaLabelItemsName: counterAriaLabelItemsName = "items", - noAnimation + noAnimation, + dialogContainerSelector } = counterProps || {}; const counterSizeStyle = getStyle(styles, size?.toString()); @@ -103,6 +104,7 @@ const AvatarGroupCounter: React.FC = ({ zIndex={1} className={cx(styles.counterContainer, counterSizeStyle, counterColorStyle)} ariaLabel={counterAriaLabel ? counterAriaLabel : `${counterValue} additional ${counterAriaLabelItemsName}`} + dialogContainerSelector={dialogContainerSelector} > {counterTooltipAvatars.map((avatar, index) => { diff --git a/packages/core/src/components/Button/__tests__/__snapshots__/Button.snapshot.test.tsx.snap b/packages/core/src/components/Button/__tests__/__snapshots__/Button.snapshot.test.tsx.snap index 3a999a911f..ed4fb66e7c 100644 --- a/packages/core/src/components/Button/__tests__/__snapshots__/Button.snapshot.test.tsx.snap +++ b/packages/core/src/components/Button/__tests__/__snapshots__/Button.snapshot.test.tsx.snap @@ -375,8 +375,7 @@ exports[`Button renders correctly renders correctly with leftIcon 1`] = ` > @@ -426,8 +425,7 @@ exports[`Button renders correctly renders correctly with rightIcon 1`] = ` > diff --git a/packages/core/src/components/Search/Search.tsx b/packages/core/src/components/Search/Search.tsx index b48b39b7f2..4f38523e19 100644 --- a/packages/core/src/components/Search/Search.tsx +++ b/packages/core/src/components/Search/Search.tsx @@ -34,6 +34,7 @@ const Search = forwardRef( onFocus, onBlur, onClear, + onKeyDown, className, ariaExpanded, ariaHasPopup, @@ -52,14 +53,11 @@ const Search = forwardRef( }); const onClearButtonClick = useCallback(() => { - if (disabled) { - return; - } - + if (disabled) return; inputRef.current?.focus?.(); clearValue(); onClear?.(); - }, [disabled, clearValue]); + }, [disabled, clearValue, onClear]); const SearchIcon = ( void; + /** + * Callback function that is called when a keyboard key is down. + */ + onKeyDown?: (event: React.KeyboardEvent) => void; } diff --git a/packages/core/src/components/Search/__tests__/Search.test.tsx b/packages/core/src/components/Search/__tests__/Search.test.tsx index ec50361156..79a0c397b7 100644 --- a/packages/core/src/components/Search/__tests__/Search.test.tsx +++ b/packages/core/src/components/Search/__tests__/Search.test.tsx @@ -24,19 +24,19 @@ describe("Search", () => { expect(queryByLabelText("Clear")).toBeNull(); }); - it("should display both the search icon and clear icon when input has value", async () => { + it("should display both the search icon and clear icon when input has value", () => { const { getByTestId, getAllByTestId } = renderSearch({ value: "Test" }); expect(getAllByTestId("icon")).toHaveLength(2); expect(getByTestId("clean-search-button")).toBeInTheDocument(); }); - it("should clear the input value when the clear icon is clicked", async () => { + it("should clear the input value when the clear icon is clicked", () => { const { getByRole, getByLabelText } = renderSearch({ value: "Test" }); userEvent.click(getByLabelText("Clear")); expect(getByRole("searchbox")).toHaveValue(""); }); - it("should display the clear icon once user inputs", async () => { + it("should display the clear icon once user inputs", () => { const { getByRole, getByTestId } = renderSearch(); userEvent.type(getByRole("searchbox"), "Test"); expect(getByTestId("clean-search-button")).toBeInTheDocument(); @@ -63,7 +63,7 @@ describe("Search", () => { expect(onClear).toHaveBeenCalled; }); - it("should debounce the onChange call", async () => { + it("should debounce the onChange call", () => { jest.useFakeTimers(); const onChange = jest.fn(); @@ -153,5 +153,33 @@ describe("Search", () => { userEvent.tab(); expect(onBlur).toHaveBeenCalled(); }); + + it("should call onKeyDown when Enter key is pressed", () => { + const onKeyDown = jest.fn(); + const { getByRole } = renderSearch({ onKeyDown }); + const input = getByRole("searchbox"); + userEvent.click(input); + userEvent.keyboard("{Enter}"); + expect(onKeyDown).toHaveBeenCalledTimes(1); + }); + + it("should not call onKeyDown when input is disabled", () => { + const onKeyDown = jest.fn(); + const { getByRole } = renderSearch({ onKeyDown, disabled: true }); + const input = getByRole("searchbox"); + userEvent.click(input); + userEvent.keyboard("{Enter}"); + expect(onKeyDown).not.toHaveBeenCalled(); + }); + + it("should call onKeyDown for each character when input is typed with content", () => { + const onKeyDown = jest.fn(); + const string = "Hello, World!"; + const { getByRole } = renderSearch({ onKeyDown }); + const input = getByRole("searchbox"); + userEvent.click(input); + userEvent.type(input, string); + expect(onKeyDown).toHaveBeenCalledTimes(string.length); + }); }); }); diff --git a/packages/core/src/components/Table/TableVirtualizedBody/TableVirtualizedBody.tsx b/packages/core/src/components/Table/TableVirtualizedBody/TableVirtualizedBody.tsx index 8150046df8..616ad4b583 100644 --- a/packages/core/src/components/Table/TableVirtualizedBody/TableVirtualizedBody.tsx +++ b/packages/core/src/components/Table/TableVirtualizedBody/TableVirtualizedBody.tsx @@ -1,4 +1,4 @@ -import React, { ComponentType, forwardRef, UIEventHandler, useCallback, useEffect } from "react"; +import React, { ComponentType, forwardRef, useCallback, useEffect } from "react"; import { VibeComponentProps } from "../../../types"; import TableBody from "../TableBody/TableBody"; import styles from "./TableVirtualizedBody.module.scss"; @@ -28,29 +28,28 @@ const TableVirtualizedBody = forwardRef( const { size, virtualizedListRef, onVirtualizedListScroll, markTableAsVirtualized } = useTable(); const { resetHoveredRow } = useTableRowMenu(); - const onAutoSizerScroll = useCallback>( - e => { + const handleOuterScroll = useCallback( + (e: Event) => { + const target = e.target as HTMLDivElement; resetHoveredRow(); - onVirtualizedListScroll(e); + onVirtualizedListScroll({ + target, + currentTarget: target + } as unknown as React.UIEvent); }, [resetHoveredRow, onVirtualizedListScroll] ); useEffect(() => { - markTableAsVirtualized(); - }, [markTableAsVirtualized]); + const scrollElement = virtualizedListRef.current; + if (!scrollElement) return; - const itemRenderer = useCallback>>( - ({ index, style: { width: _width, ...style } }) => { - const currentItem = items[index]; - const element = rowRenderer(currentItem); - return React.cloneElement(element, { - style: { ...style, ...element.props?.style }, - key: index - }); - }, - [items, rowRenderer] - ); + scrollElement.addEventListener("scroll", handleOuterScroll); + + return () => { + scrollElement.removeEventListener("scroll", handleOuterScroll); + }; + }, [handleOuterScroll]); const handleVirtualizedVerticalScroll = useCallback( ({ @@ -67,6 +66,22 @@ const TableVirtualizedBody = forwardRef( [onScroll] ); + const itemRenderer = useCallback>>( + ({ index, style: { width: _width, ...style } }) => { + const currentItem = items[index]; + const element = rowRenderer(currentItem); + return React.cloneElement(element, { + style: { ...style, ...element.props?.style }, + key: index + }); + }, + [items, rowRenderer] + ); + + useEffect(() => { + markTableAsVirtualized(); + }, [markTableAsVirtualized]); + return ( {items?.length && ( - + {({ height, width }: AutoSizerSize) => ( maxLength); const ariaDescribedby = useMemo( () => [helpTextId, allowExceedingMaxLengthTextId].filter(id => !!id).join(" ") || undefined, @@ -49,6 +48,7 @@ const TextArea = forwardRef( ); const [characterCount, setCharacterCount] = useState(value?.length || 0); + const isErrorState = error || (typeof maxLength === "number" && characterCount > maxLength); const handleOnChange = useCallback( (event: React.ChangeEvent) => { @@ -88,26 +88,28 @@ const TextArea = forwardRef( required={required} rows={numRows} className={cx(styles.textArea, [styles[size]], { [styles.resize]: resize })} - aria-invalid={error} + aria-invalid={isErrorState} aria-describedby={ariaDescribedby} onChange={handleOnChange} /> - - {helpText && ( - - {helpText} - - )} - {showCharCount && ( - <> - - {characterCount} - {typeof maxLength === "number" && `/${maxLength}`} + {(showCharCount || helpText) && ( + + {helpText && ( + + {helpText} - - - )} - + )} + {showCharCount && ( + <> + + {characterCount} + {typeof maxLength === "number" && `/${maxLength}`} + + + + )} + + )} ); } diff --git a/packages/core/src/components/TextArea/__tests__/TextArea.test.tsx b/packages/core/src/components/TextArea/__tests__/TextArea.test.tsx index ca6a3d7d1c..b504edc31b 100644 --- a/packages/core/src/components/TextArea/__tests__/TextArea.test.tsx +++ b/packages/core/src/components/TextArea/__tests__/TextArea.test.tsx @@ -143,6 +143,7 @@ describe("TextArea", () => { expect(input).toHaveValue("12345678910"); expect(charCount).toHaveTextContent("11/10"); + expect(input).toHaveAttribute("aria-invalid", "true"); }); it("should allow text removal when character limit is exceeded", () => { diff --git a/packages/core/src/components/TextField/TextField.tsx b/packages/core/src/components/TextField/TextField.tsx index 12a083fac5..e394e5889c 100644 --- a/packages/core/src/components/TextField/TextField.tsx +++ b/packages/core/src/components/TextField/TextField.tsx @@ -224,7 +224,7 @@ const TextField: VibeComponent & { }, [disabled, clearOnIconClick, onIconClick, currentStateIconName, controlled, onChangeCallback, clearValue]); const validationClass = useMemo(() => { - if (typeof maxLength === "number" && inputValue.length > maxLength) { + if (typeof maxLength === "number" && inputValue && inputValue.length > maxLength) { return FEEDBACK_CLASSES.error; } @@ -233,7 +233,7 @@ const TextField: VibeComponent & { } const status = isRequiredAndEmpty ? "error" : validation.status; return FEEDBACK_CLASSES[status]; - }, [validation, isRequiredAndEmpty, inputValue]); + }, [maxLength, validation, isRequiredAndEmpty, inputValue]); const hasIcon = iconName || secondaryIconName; const shouldShowExtraText = showCharCount || (validation && validation.text) || isRequiredAndEmpty; diff --git a/packages/core/src/storybook/stand-alone-documentaion/migration-guide.mdx b/packages/core/src/storybook/stand-alone-documentaion/migration-guide.mdx index 03fdf41318..9b2c6abad0 100644 --- a/packages/core/src/storybook/stand-alone-documentaion/migration-guide.mdx +++ b/packages/core/src/storybook/stand-alone-documentaion/migration-guide.mdx @@ -45,9 +45,7 @@ The `monday-ui-react-core` package has been renamed to `@vibe/core` to better re ### Reduced bundle size -By removing CommonJS support, dividing the library into packages, and optimizing the library exports - the bundle size has been reduced to [fill in] kb, resulting in faster load times and improved performance. - -{/* bundle phobia image? */} +By removing CommonJS support, dividing the library into packages, and optimizing the library exports - the bundle size has been reduced by 43%, resulting in faster load times and improved performance. ### New Typography System @@ -127,9 +125,9 @@ These steps will guide you through the migration process: ## Breaking Changes 🚨 -Several breaking changes have been made to optimize and streamline the library. These changes include the removal of deprecated components, updates to component APIs, and overall enhancements. The following are the most significant changes that require manual intervention, **in addition to the automatic migration script**. +Several breaking changes have been made to optimize and streamline the library. These changes include the removal of deprecated components, updates to component APIs, and overall enhancements. -For the complete set of changes, refer to the [Vibe 3 changelog](https://github.com/mondaycom/vibe/blob/master/packages/core/docs/vibe-3-changelog.md). +Please note that the following changes are **complementary to the migration script** and require manual intervention, assuming that the migration script has ran successfully. If you prefer migrating entirely manually (without the `@vibe/codemod` script), please refer to the [Complete Vibe 3 changelog](https://github.com/mondaycom/vibe/blob/master/packages/core/docs/vibe-3-changelog.md). ### General diff --git a/packages/icons/package.json b/packages/icons/package.json index 93f197690f..dbcea110cf 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,7 +1,7 @@ { "name": "@vibe/icons", - "version": "0.1.0", - "description": "Vibe's icon library", + "version": "1.0.0-rc.0", + "description": "Vibe's Icon Library", "repository": { "type": "git", "url": "git+https://github.com/mondaycom/vibe.git", diff --git a/packages/icons/src/iconsMetaData.ts b/packages/icons/src/iconsMetaData.ts index 8dd333f8b8..1c3775229d 100644 --- a/packages/icons/src/iconsMetaData.ts +++ b/packages/icons/src/iconsMetaData.ts @@ -14,10 +14,18 @@ export default [ // plop_marker:icon_metadata { - name: "ThumbsDown", - file: "ThumbsDown.svg", - description: "Meant to indicate dislike in intake forms and feedback buttons. Can come as a supplement to thumbs up.", - tags: "Thumbs, Down, Dislike, Feedback" + name: "PinFull", + file: "PinFull.svg", + description: "Use to mark item as pinned.", + tags: "Pinned, Pinning" + }, + + { + name: "ThumbsDown", + file: "ThumbsDown.svg", + description: + "Meant to indicate dislike in intake forms and feedback buttons. Can come as a supplement to thumbs up.", + tags: "Thumbs, Down, Dislike, Feedback" }, { @@ -1831,7 +1839,7 @@ export default [ name: "WhatsNew", file: "WhatsNew.svg", description: "Whats New", - tags: "WhatsNew, New", + tags: "WhatsNew, New, Gift", category: [PLATFORM] }, diff --git a/packages/icons/src/svg/PinFull.svg b/packages/icons/src/svg/PinFull.svg new file mode 100644 index 0000000000..afad4f381c --- /dev/null +++ b/packages/icons/src/svg/PinFull.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/icons/src/svg/Switcher.svg b/packages/icons/src/svg/Switcher.svg index 3502be3030..8cbbb3e8ae 100644 --- a/packages/icons/src/svg/Switcher.svg +++ b/packages/icons/src/svg/Switcher.svg @@ -1,3 +1,10 @@ - + + + + + + + + diff --git a/packages/icons/src/svg/TextSmall.svg b/packages/icons/src/svg/TextSmall.svg index 6a54ad7b04..4a1a7a5a4f 100644 --- a/packages/icons/src/svg/TextSmall.svg +++ b/packages/icons/src/svg/TextSmall.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/icons/src/svg/WhatsNew.svg b/packages/icons/src/svg/WhatsNew.svg index f82ddb196c..15a7c369d0 100644 --- a/packages/icons/src/svg/WhatsNew.svg +++ b/packages/icons/src/svg/WhatsNew.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/style/CHANGELOG.md b/packages/style/CHANGELOG.md index 8661db66e7..da96bc1a6e 100644 --- a/packages/style/CHANGELOG.md +++ b/packages/style/CHANGELOG.md @@ -3,6 +3,55 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.20.0](https://github.com/mondaycom/vibe/compare/monday-ui-style@0.19.0...monday-ui-style@0.20.0) (2024-11-20) + + +### Features + +* **Switcher:** update icon ([#2599](https://github.com/mondaycom/vibe/issues/2599)) ([cdac401](https://github.com/mondaycom/vibe/commit/cdac401ee11c14c2ae3ca2d4fe62aa579b2dd1c0)) + + + + + +# [0.19.0](https://github.com/mondaycom/vibe/compare/monday-ui-style@0.18.1...monday-ui-style@0.19.0) (2024-11-19) + + +### Features + +* **Switcher:** update icon ([#2597](https://github.com/mondaycom/vibe/issues/2597)) ([3c045b1](https://github.com/mondaycom/vibe/commit/3c045b18caf3dad36787c5ee9e84ebb3f71425b2)) + + + + + +## [0.18.1](https://github.com/mondaycom/vibe/compare/monday-ui-style@0.18.0...monday-ui-style@0.18.1) (2024-11-18) + + +### Bug Fixes + +* **Switcher:** revert icon change to older version ([#2596](https://github.com/mondaycom/vibe/issues/2596)) ([202e5a6](https://github.com/mondaycom/vibe/commit/202e5a6a1e20b3fc9c9e20185d8d46fc713650b6)) + + + + + +# [0.18.0](https://github.com/mondaycom/vibe/compare/monday-ui-style@0.17.0...monday-ui-style@0.18.0) (2024-11-18) + + +### Bug Fixes + +* **WhatsNew:** update icon ([#2592](https://github.com/mondaycom/vibe/issues/2592)) ([fbcb99f](https://github.com/mondaycom/vibe/commit/fbcb99f257b624ee7a64eb270c30292b25f6e2ef)) + + +### Features + +* **PinFull:** new icon ([#2589](https://github.com/mondaycom/vibe/issues/2589)) ([1e34a3c](https://github.com/mondaycom/vibe/commit/1e34a3cff3ee9d1be3d62643258fbca20a2bed83)) + + + + + # [0.17.0](https://github.com/mondaycom/vibe/compare/monday-ui-style@0.16.0...monday-ui-style@0.17.0) (2024-11-14) diff --git a/packages/style/package.json b/packages/style/package.json index 2bfecae071..7ef60c0f8b 100644 --- a/packages/style/package.json +++ b/packages/style/package.json @@ -1,6 +1,6 @@ { "name": "monday-ui-style", - "version": "0.17.0", + "version": "0.20.0", "description": "Monday UI CSS Foundations", "keywords": [ "CSS", diff --git a/packages/testkit/.eslintrc.js b/packages/testkit/.eslintrc.js new file mode 100644 index 0000000000..4e7dd544f9 --- /dev/null +++ b/packages/testkit/.eslintrc.js @@ -0,0 +1,59 @@ +const defaultPlugins = ["prettier"]; +const defaultExtends = ["eslint:recommended", "plugin:prettier/recommended"]; +const defaultRules = { + "prettier/prettier": "error" +}; + +const commonJsPlugins = defaultPlugins; +const commonJsExtends = defaultExtends; +const commonJsRules = { + ...defaultRules, + "no-unused-vars": ["error", { argsIgnorePattern: "^_" }] +}; + +const commonTsPlugins = [...defaultPlugins, "@typescript-eslint"]; +const commonTsExtends = [...defaultExtends, "plugin:@typescript-eslint/recommended"]; +const commonTsRules = { + ...defaultRules, + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + "@typescript-eslint/no-explicit-any": "warn" +}; + +module.exports = { + ignorePatterns: ["node_modules", "dist"], + parserOptions: { + sourceType: "module" + }, + env: { + es2021: true, + node: true + }, + overrides: [ + { + files: ["**/*.js"], + extends: commonJsExtends, + plugins: commonJsPlugins, + rules: commonJsRules + }, + { + files: ["**/*.ts"], + parser: "@typescript-eslint/parser", + plugins: commonTsPlugins, + extends: commonTsExtends, + rules: commonTsRules + }, + { + files: ["./__tests__/*.test.js"], + plugins: [...commonJsPlugins, "playwright"], + extends: [...commonJsExtends, "plugin:playwright/recommended"], + rules: commonJsRules + }, + { + files: ["./__tests__/*.test.ts"], + parser: "@typescript-eslint/parser", + plugins: [...commonTsPlugins, "playwright"], + extends: [...commonTsExtends, "plugin:playwright/recommended"], + rules: commonTsRules + } + ] +}; diff --git a/packages/testkit/BaseElement.ts b/packages/testkit/BaseElement.ts index ae77c4814b..a66d4ce0ad 100644 --- a/packages/testkit/BaseElement.ts +++ b/packages/testkit/BaseElement.ts @@ -4,16 +4,16 @@ import { test, Page, Locator } from "@playwright/test"; * Class representing a base element for Playwright tests. */ export class BaseElement { - page : Page; - locator : Locator; - elementReportName: String + page: Page; + locator: Locator; + elementReportName: string; /** * Create a BaseElement. * @param {Object} page - The Playwright page object. * @param {Object} locator - The locator for the element. * @param {string} elementReportName - The name for reporting purposes. */ - constructor(page: Page, locator: Locator, elementReportName: String) { + constructor(page: Page, locator: Locator, elementReportName: string) { this.page = page; this.locator = locator; this.elementReportName = elementReportName; @@ -31,11 +31,12 @@ export class BaseElement { * Wait for the list elements to stabilize (i.e., the count of items remains constant for a specified duration). * @returns {Promise} */ - async waitForElementsGroup(locator: Locator, elementReportName: String): Promise { + async waitForElementsGroup(locator: Locator, elementReportName: string): Promise { await test.step(`Wait for ${elementReportName} items to stabilize`, async () => { let previousCount = 0; let stableCountTime = 0; const stabilizationTimeMs = 500; + // eslint-disable-next-line no-constant-condition while (true) { const currentCount = await this.locator.locator(locator).count(); @@ -58,12 +59,12 @@ export class BaseElement { * @returns {Promise} - Returns true if the element is enabled, otherwise false. */ async isEnabled(): Promise { - let isEnabled: boolean = false; - await test.step(`Return if ${this.elementReportName} is enabled`, async () => { - isEnabled = await this.locator.isEnabled(); - return isEnabled; - }); + let isEnabled = false; + await test.step(`Return if ${this.elementReportName} is enabled`, async () => { + isEnabled = await this.locator.isEnabled(); return isEnabled; + }); + return isEnabled; } /** @@ -75,15 +76,19 @@ export class BaseElement { await this.locator.scrollIntoViewIfNeeded(); }); } - async getAttributeValue(attributeName: string, options: any = { timeout: 10000, pollInterval: 500 }) : Promise { + async getAttributeValue( + attributeName: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options: any = { timeout: 10000, pollInterval: 500 } + ): Promise { let attributeValue = null; - + await test.step(`Get attribute ${attributeName} of ${this.elementReportName}`, async () => { const startTime = Date.now(); - + while (Date.now() - startTime < options.timeout) { attributeValue = await this.locator.getAttribute(attributeName); - + if (attributeValue !== null) { break; } @@ -98,8 +103,8 @@ export class BaseElement { return attributeValue; } - async getText() : Promise { - let text: string|undefined; + async getText(): Promise { + let text: string | undefined; await test.step(`Get text of ${this.elementReportName}`, async () => { text = await this.locator.innerText(); return text; @@ -113,14 +118,14 @@ export class BaseElement { }); } - async waitForAbsence() : Promise{ + async waitForAbsence(): Promise { await test.step(`Wait for ${this.elementReportName} to be absent`, async () => { await this.waitFor({ state: "detached" }); }); } - async count() : Promise{ - let count: number=0; + async count(): Promise { + let count = 0; await test.step(`Count elements matching ${this.elementReportName}`, async () => { count = await this.locator.count(); }); diff --git a/packages/testkit/CHANGELOG.md b/packages/testkit/CHANGELOG.md index 14a92da187..bd2ca2b0fc 100644 --- a/packages/testkit/CHANGELOG.md +++ b/packages/testkit/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.0.5](https://github.com/mondaycom/vibe/compare/@vibe/testkit@1.0.4...@vibe/testkit@1.0.5) (2024-11-20) + +**Note:** Version bump only for package @vibe/testkit + + + + + +## [1.0.4](https://github.com/mondaycom/vibe/compare/@vibe/testkit@1.0.3...@vibe/testkit@1.0.4) (2024-11-19) + +**Note:** Version bump only for package @vibe/testkit + + + + + ## [1.0.3](https://github.com/mondaycom/vibe/compare/@vibe/testkit@1.0.2...@vibe/testkit@1.0.3) (2024-11-14) **Note:** Version bump only for package @vibe/testkit diff --git a/packages/testkit/__TESTS__/button.spec.js b/packages/testkit/__TESTS__/button.spec.js deleted file mode 100644 index 1f3a94e435..0000000000 --- a/packages/testkit/__TESTS__/button.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { Button } from '../buttons/Button'; -import { buttonStory } from './utils/url-helper'; - -test('should fire a click event and log to console', async ({ page }) => { - // Navigate to the Storybook page with the component - await page.goto(buttonStory, {timeout: 100000}); - // Locate the iframe where the button is rendered - const frame = page.frameLocator("[id='storybook-preview-iframe']"); - const button = new Button(page, frame.locator('button[data-testid="button"]'), 'Button'); - - //TODO - find a better way to wait for the storybook to load - while (await button.locator.isVisible() === false) { - await page.waitForTimeout(30000); - await page.reload(); - if (await button.locator.isVisible() === true) { - break; - } - } - // Add a listener to capture console logs - let consoleMessage = ''; - page.on('console', async (msg) => { - const values = await Promise.all(msg.args().map(arg => arg.jsonValue())); - consoleMessage = values.join(' '); - }); - - // Attach a click event listener that logs a message to the console - await button.locator.evaluate((buttonElement) => { - buttonElement.addEventListener('click', () => { - console.log('Button clicked'); // Log to console when clicked - }); - }); - // Click the button - await button.click(); - - // Wait a bit to ensure the console log is captured - await page.waitForTimeout(500); - - // Verify the console log contains the expected message - expect(consoleMessage).toContain('Button clicked'); -}); diff --git a/packages/testkit/__TESTS__/checkbox.spec.js b/packages/testkit/__TESTS__/checkbox.spec.js deleted file mode 100644 index 6c03dfbeee..0000000000 --- a/packages/testkit/__TESTS__/checkbox.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -import { test, expect } from "@playwright/test"; -import { Checkbox } from "../inputs/Checkbox"; -import { checkboxStory } from "./utils/url-helper"; - -test.describe("menuButton Class with Storybook", () => { - let checkbox; - - test.beforeEach(async ({ page }) => { - await page.goto(checkboxStory); - const frame = page.frameLocator("[id='storybook-preview-iframe']"); - const checkboxLocator = frame.locator('[data-testid="checkbox-checkbox"]'); - checkbox = new Checkbox(page, checkboxLocator, "Test checkbox button"); - }); - - test("set checkbox", async ({page}) => { - if (await checkbox.isChecked()){ - await checkbox.setChecked(false); - expect(await checkbox.isChecked()).toBe(false); - }else { - await checkbox.setChecked(true); - expect(await checkbox.isChecked()).toBe(true); - } - }); -}); \ No newline at end of file diff --git a/packages/testkit/__tests__/button.test.js b/packages/testkit/__tests__/button.test.js new file mode 100644 index 0000000000..7f60935877 --- /dev/null +++ b/packages/testkit/__tests__/button.test.js @@ -0,0 +1,43 @@ +import { test, expect } from "@playwright/test"; +import { Button } from "../buttons/Button"; +import { buttonStory } from "./utils/url-helper"; + +test("should fire a click event and log to console", async ({ page }) => { + // Navigate to the Storybook page with the component + await page.goto(buttonStory, { timeout: 100000 }); + // Locate the iframe where the button is rendered + const frame = page.frameLocator("[id='storybook-preview-iframe']"); + const button = new Button(page, frame.locator('button[data-testid="button"]'), "Button"); + + //TODO - find a better way to wait for the storybook to load + while ((await button.locator.isVisible()) === false) { + // eslint-disable-next-line playwright/no-wait-for-timeout + await page.waitForTimeout(30000); + await page.reload(); + // eslint-disable-next-line playwright/no-conditional-in-test + if ((await button.locator.isVisible()) === true) { + break; + } + } + // Add a listener to capture console logs + let consoleMessage = ""; + page.on("console", async msg => { + const values = await Promise.all(msg.args().map(arg => arg.jsonValue())); + consoleMessage = values.join(" "); + }); + + // Attach a click event listener that logs a message to the console + await button.locator.evaluate(buttonElement => { + buttonElement.addEventListener("click", () => { + console.log("Button clicked"); // Log to console when clicked + }); + }); + // Click the button + await button.click(); + + // eslint-disable-next-line playwright/no-wait-for-timeout -- Wait a bit to ensure the console log is captured + await page.waitForTimeout(500); + + // Verify the console log contains the expected message + expect(consoleMessage).toContain("Button clicked"); +}); diff --git a/packages/testkit/__TESTS__/buttonGroup.spec.js b/packages/testkit/__tests__/buttonGroup.test.js similarity index 89% rename from packages/testkit/__TESTS__/buttonGroup.spec.js rename to packages/testkit/__tests__/buttonGroup.test.js index ba07572ca8..ad6e4c62d5 100644 --- a/packages/testkit/__TESTS__/buttonGroup.spec.js +++ b/packages/testkit/__tests__/buttonGroup.test.js @@ -2,7 +2,6 @@ import { test, expect } from "@playwright/test"; import { ButtonGroup } from "../buttons/ButtonGroup"; import { buttonGroupStory } from "./utils/url-helper"; - test.describe("ButtonGroup Class with Storybook", () => { let buttonGroup; @@ -15,10 +14,11 @@ test.describe("ButtonGroup Class with Storybook", () => { // Locate the button group inside the iframe const buttonGroupLocator = frame.locator('div[data-testid="button-group"]'); - while (await buttonGroupLocator.isVisible() === false) { + while ((await buttonGroupLocator.isVisible()) === false) { + // eslint-disable-next-line playwright/no-wait-for-timeout await page.waitForTimeout(30000); await page.reload(); - if (await buttonGroupLocator.isVisible() === true) { + if ((await buttonGroupLocator.isVisible()) === true) { break; } } @@ -60,7 +60,7 @@ test.describe("ButtonGroup Class with Storybook", () => { // Click the button await buttonGroup.click("Beta"); - // Wait a bit to ensure the console log is captured + // eslint-disable-next-line playwright/no-wait-for-timeout -- Wait a bit to ensure the console log is captured await page.waitForTimeout(500); // Verify the console message contains the expected log diff --git a/packages/testkit/__tests__/checkbox.test.js b/packages/testkit/__tests__/checkbox.test.js new file mode 100644 index 0000000000..d1286d7122 --- /dev/null +++ b/packages/testkit/__tests__/checkbox.test.js @@ -0,0 +1,28 @@ +import { test, expect } from "@playwright/test"; +import { Checkbox } from "../inputs/Checkbox"; +import { checkboxStory } from "./utils/url-helper"; + +test.describe("menuButton Class with Storybook", () => { + let checkbox; + + test.beforeEach(async ({ page }) => { + await page.goto(checkboxStory); + const frame = page.frameLocator("[id='storybook-preview-iframe']"); + const checkboxLocator = frame.locator('[data-testid="checkbox-checkbox"]'); + checkbox = new Checkbox(page, checkboxLocator, "Test checkbox button"); + }); + + // eslint-disable-next-line no-unused-vars + test("set checkbox", async ({ page }) => { + // eslint-disable-next-line playwright/no-conditional-in-test + if (await checkbox.isChecked()) { + await checkbox.setChecked(false); + // eslint-disable-next-line playwright/prefer-web-first-assertions, playwright/no-conditional-expect + expect(await checkbox.isChecked()).toBe(false); + } else { + await checkbox.setChecked(true); + // eslint-disable-next-line playwright/prefer-web-first-assertions, playwright/no-conditional-expect + expect(await checkbox.isChecked()).toBe(true); + } + }); +}); diff --git a/packages/testkit/__TESTS__/dropdown.spec.js b/packages/testkit/__tests__/dropdown.test.js similarity index 67% rename from packages/testkit/__TESTS__/dropdown.spec.js rename to packages/testkit/__tests__/dropdown.test.js index cdb58d4ca7..3723dfe173 100644 --- a/packages/testkit/__TESTS__/dropdown.spec.js +++ b/packages/testkit/__tests__/dropdown.test.js @@ -12,9 +12,12 @@ test.describe("dropdown Class with Storybook", () => { DropDown = new Dropdown(page, DropDownLocator, "Test DropDown"); }); - test("set dropdown value", async ({ page }) => { - await page.waitForTimeout(3000); + test.fixme("set dropdown value", async ({ page }) => { + // eslint-disable-next-line playwright/no-wait-for-timeout -- extended wait for interaction test to finish + await page.waitForTimeout(10000); + await DropDown.inputField.setText(""); await DropDown.selectItem("2"); + // eslint-disable-next-line playwright/no-wait-for-timeout await page.waitForTimeout(500); expect(await DropDown.getText()).toContain("2"); }); diff --git a/packages/testkit/__TESTS__/menuButton.spec.js b/packages/testkit/__tests__/menuButton.test.js similarity index 81% rename from packages/testkit/__TESTS__/menuButton.spec.js rename to packages/testkit/__tests__/menuButton.test.js index 42efcc601f..1f45a6bb45 100644 --- a/packages/testkit/__TESTS__/menuButton.spec.js +++ b/packages/testkit/__tests__/menuButton.test.js @@ -12,11 +12,13 @@ test.describe("menuButton Class with Storybook", () => { menuButton = new MenuButton(page, menubuttonLocator, "Test menu button"); }); - test("should open and close menu", async ({page}) => { + test("should open and close menu", async ({ page }) => { await menuButton.openMenu(); + // eslint-disable-next-line playwright/no-wait-for-timeout await page.waitForTimeout(500); expect(await menuButton.isExpanded()).toBe(true); await menuButton.closeMenu(); + // eslint-disable-next-line playwright/no-wait-for-timeout await page.waitForTimeout(500); expect(await menuButton.isExpanded()).toBe(false); }); diff --git a/packages/testkit/__TESTS__/textArea.spec.js b/packages/testkit/__tests__/textArea.test.js similarity index 76% rename from packages/testkit/__TESTS__/textArea.spec.js rename to packages/testkit/__tests__/textArea.test.js index c57cc2b33f..75f4f4e4a8 100644 --- a/packages/testkit/__TESTS__/textArea.spec.js +++ b/packages/testkit/__tests__/textArea.test.js @@ -4,7 +4,7 @@ import { textAreaStory } from "./utils/url-helper"; test.describe("textArea Class with Storybook", () => { let textArea; - let textAreaLocator + let textAreaLocator; test.beforeEach(async ({ page }) => { await page.goto(textAreaStory); @@ -13,8 +13,10 @@ test.describe("textArea Class with Storybook", () => { textArea = new TextArea(page, textAreaLocator, "Test text field"); }); - test("set text in textarea", async ({page}) => { + // eslint-disable-next-line no-unused-vars + test("set text in textarea", async ({ page }) => { await textArea.setText("Test Text"); + // eslint-disable-next-line playwright/missing-playwright-await expect(textArea.textAreaInput.locator).toHaveValue("Test Text"); }); -}); \ No newline at end of file +}); diff --git a/packages/testkit/__TESTS__/textfield.spec.js b/packages/testkit/__tests__/textfield.test.js similarity index 77% rename from packages/testkit/__TESTS__/textfield.spec.js rename to packages/testkit/__tests__/textfield.test.js index b8235a2e61..2418126b83 100644 --- a/packages/testkit/__TESTS__/textfield.spec.js +++ b/packages/testkit/__tests__/textfield.test.js @@ -4,7 +4,7 @@ import { textfieldStory } from "./utils/url-helper"; test.describe("textArea Class with Storybook", () => { let textField; - let textfieldLocator + let textfieldLocator; test.beforeEach(async ({ page }) => { await page.goto(textfieldStory); @@ -13,9 +13,11 @@ test.describe("textArea Class with Storybook", () => { textField = new TextField(page, textfieldLocator, "Test text field"); }); - test("set text in text field", async ({page}) => { + // eslint-disable-next-line no-unused-vars + test("set text in text field", async ({ page }) => { await textField.setText("Test Text"); await textField.exitEditMode(); + // eslint-disable-next-line playwright/missing-playwright-await expect(textField.locator).toHaveValue("Test Text"); }); -}); \ No newline at end of file +}); diff --git a/packages/testkit/__TESTS__/utils/url-helper.js b/packages/testkit/__tests__/utils/url-helper.js similarity index 100% rename from packages/testkit/__TESTS__/utils/url-helper.js rename to packages/testkit/__tests__/utils/url-helper.js diff --git a/packages/testkit/buttons/Button.ts b/packages/testkit/buttons/Button.ts index 796d4c21d6..bcf33bcba8 100644 --- a/packages/testkit/buttons/Button.ts +++ b/packages/testkit/buttons/Button.ts @@ -7,7 +7,7 @@ import { BaseElement } from "../BaseElement"; * Extends the BaseElement class. */ export class Button extends BaseElement { - override page : Page; + override page: Page; override locator: Locator; override elementReportName: string; /** @@ -27,7 +27,7 @@ export class Button extends BaseElement { * Execute click action on the element. * @returns {Promise} */ - async click() : Promise { + async click(): Promise { await test.step(`Click on: ${this.elementReportName}`, async () => { await this.locator.click(); }); diff --git a/packages/testkit/buttons/ButtonGroup.ts b/packages/testkit/buttons/ButtonGroup.ts index c7b6145878..75da7dbf37 100644 --- a/packages/testkit/buttons/ButtonGroup.ts +++ b/packages/testkit/buttons/ButtonGroup.ts @@ -7,14 +7,12 @@ import { Button } from "./Button"; * Extends the BaseElement class. */ export class ButtonGroup extends BaseElement { - - override page : Page; + override page: Page; override locator: Locator; override elementReportName: string; items: Button[]; buttonsInitialized: boolean; - constructor(page: Page, locator: Locator, elementReportName: string) { super(page, locator, elementReportName); this.page = page; @@ -54,13 +52,12 @@ export class ButtonGroup extends BaseElement { }); } - /** * Get a button by its name. * @param {string} buttonName - The name of the button to retrieve. * @returns {Button} The button with the specified name. */ - getButtonByName(buttonName: string): Button|undefined { + getButtonByName(buttonName: string): Button | undefined { if (!buttonName || typeof buttonName !== "string") { throw new Error("Invalid button name provided"); } @@ -75,14 +72,14 @@ export class ButtonGroup extends BaseElement { */ async click(buttonName: string): Promise { await this.initializeButtonsIfNeeded(); - + const button = this.getButtonByName(buttonName); - + // Throw an error if the button is not found if (!button) { throw new Error(`Invalid button name provided: ${buttonName}`); } - + await button.click(); } } diff --git a/packages/testkit/buttons/MenuButton.ts b/packages/testkit/buttons/MenuButton.ts index 964eada883..05b4f14942 100644 --- a/packages/testkit/buttons/MenuButton.ts +++ b/packages/testkit/buttons/MenuButton.ts @@ -9,7 +9,8 @@ export class MenuButton extends Button { override locator: Locator; override elementReportName: string; button: Button; - menu: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + menu: any; /** * Create a MenuButton. @@ -18,6 +19,7 @@ export class MenuButton extends Button { * @param {string} elementReportName - The name for reporting purposes. * @param {any} menuType - The type of menu associated with the button. */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(page: Page, locator: Locator, elementReportName: string, menuType: any) { super(page, locator, elementReportName); this.page = page; diff --git a/packages/testkit/buttons/SplitButton.ts b/packages/testkit/buttons/SplitButton.ts index 434df0f077..729774dedd 100644 --- a/packages/testkit/buttons/SplitButton.ts +++ b/packages/testkit/buttons/SplitButton.ts @@ -5,9 +5,9 @@ import { Button } from "./Button"; * Class representing a split button that extends the Button class. */ export class SplitButton extends Button { - override page: Page; - override locator: Locator; - override elementReportName: string; + override page: Page; + override locator: Locator; + override elementReportName: string; /** * Create a SplitButton. diff --git a/packages/testkit/index.ts b/packages/testkit/index.ts index b903f04a4b..c74e1ef1eb 100644 --- a/packages/testkit/index.ts +++ b/packages/testkit/index.ts @@ -5,4 +5,4 @@ export * from "./navigation"; export * from "./popover"; export * from "./text"; export * from "./utils/common-actions"; -export * from "./BaseElement"; \ No newline at end of file +export * from "./BaseElement"; diff --git a/packages/testkit/navigation/List.ts b/packages/testkit/navigation/List.ts index 7d4ba00093..531d95fb5d 100644 --- a/packages/testkit/navigation/List.ts +++ b/packages/testkit/navigation/List.ts @@ -76,4 +76,4 @@ export class List extends BaseElement { await item.scrollIntoView(); await item.click(); } -} \ No newline at end of file +} diff --git a/packages/testkit/navigation/TabList.ts b/packages/testkit/navigation/TabList.ts index af5b1eb19e..064465e707 100644 --- a/packages/testkit/navigation/TabList.ts +++ b/packages/testkit/navigation/TabList.ts @@ -7,7 +7,7 @@ import { Tab } from "./Tab"; * Extends the BaseElement class. */ export class TabList extends BaseElement { - private items: Tab[]; // List of tabs + private items: Tab[]; // List of tabs private tabsInitialized: boolean; /** @@ -44,7 +44,7 @@ export class TabList extends BaseElement { await this.waitForElementsGroup(this.locator.locator("li"), this.elementReportName); const tabElements = await this.locator.locator("li").all(); this.items = await Promise.all( - tabElements.map(async (locator) => { + tabElements.map(async locator => { const tabName = await locator.innerText(); return new Tab(this.page, locator.getByText(`${tabName}`), `Tab Item: ${tabName}`); }) diff --git a/packages/testkit/package.json b/packages/testkit/package.json index 4e51d5acd1..44d978badd 100644 --- a/packages/testkit/package.json +++ b/packages/testkit/package.json @@ -1,11 +1,20 @@ { "name": "@vibe/testkit", - "version": "1.0.3", + "version": "1.0.5", "description": "Vibe e2e testing toolkit", "keywords": [ "TESTING", "E2E" ], + "module": "dist/esm/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "require": "./dist/index.js", + "import": "./dist/esm/index.js", + "types": "./dist/index.d.ts" + } + }, "author": "monday.com", "homepage": "https://github.com/mondaycom/vibe#readme", "license": "ISC", @@ -17,13 +26,24 @@ }, "scripts": { "test:e2e": "npx playwright test", - "build": "tsc", - "start-server": "yarn lerna run storybook --scope=@vibe/core" + "build:cjs": "tsc", + "build:esm": "tsc --project tsconfig.esm.json", + "build": "yarn build:cjs && yarn build:esm", + "start-server": "yarn lerna run storybook --scope=@vibe/core", + "lint": "eslint . --max-warnings=0", + "lint:fix": "yarn lint -- --fix" }, "bugs": { "url": "https://github.com/mondaycom/vibe/issues" }, "devDependencies": { - "@playwright/test": "1.45.3" + "@playwright/test": "1.45.3", + "@typescript-eslint/eslint-plugin": "^5.36.1", + "@typescript-eslint/parser": "^5.36.1", + "eslint": "^8.23.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-playwright": "^2.1.0", + "eslint-plugin-prettier": "^4.0.0", + "prettier": "^2.5.1" } } diff --git a/packages/testkit/pickers/ColorPicker.ts b/packages/testkit/pickers/ColorPicker.ts index e45a501f59..3abfbc4774 100644 --- a/packages/testkit/pickers/ColorPicker.ts +++ b/packages/testkit/pickers/ColorPicker.ts @@ -33,7 +33,7 @@ export class ColorPicker extends BaseElement { * Get the currently selected color. * @returns {Promise} The selected color. */ - async getSelectedColor(): Promise { + async getSelectedColor(): Promise { const selectedColor = new BaseElement( this.page, this.locator.locator("//*[contains(@class, 'selectedColor')]"), @@ -41,6 +41,6 @@ export class ColorPicker extends BaseElement { ); const dataTestIdAttr = await selectedColor.getAttributeValue("data-testId"); const parts = dataTestIdAttr?.split("_"); - return parts?.slice(-2).join("_"); // Returning the last two parts as the selected color + return parts?.slice(-2).join("_"); // Returning the last two parts as the selected color } } diff --git a/packages/testkit/playwright.config.ts b/packages/testkit/playwright.config.ts index 2fbc9db00c..2ee1b108d2 100644 --- a/packages/testkit/playwright.config.ts +++ b/packages/testkit/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from '@playwright/test'; +import { defineConfig } from "@playwright/test"; import path from "path"; /** @@ -8,17 +8,17 @@ export default defineConfig({ fullyParallel: false, workers: 1, reporter: [["html", { open: "never", outputFolder: path.join(process.cwd(), "/reports") }]], - + // Run your local dev server before starting the tests webServer: { - command: 'yarn start-server', - url: 'http://127.0.0.1:7008', + command: "yarn start-server", + url: "http://127.0.0.1:7008", reuseExistingServer: !process.env.CI, timeout: 120 * 1000, - stdout: 'ignore', - stderr: 'pipe', + stdout: "ignore", + stderr: "pipe" }, - + use: { headless: true, baseURL: "http://127.0.0.1:7008", @@ -30,5 +30,5 @@ export default defineConfig({ args: ["--disable-web-security"] } }, - timeout: 180 * 1000, + timeout: 180 * 1000 }); diff --git a/packages/testkit/popover/Dialog.ts b/packages/testkit/popover/Dialog.ts index d45f52a384..e552b4d7c6 100644 --- a/packages/testkit/popover/Dialog.ts +++ b/packages/testkit/popover/Dialog.ts @@ -1,6 +1,6 @@ import { Page, Locator } from "@playwright/test"; import { BaseElement } from "../BaseElement"; -import { Button } from "../buttons/Button"; +import { Button } from "../buttons/Button"; /** * Class representing a Dialog element. diff --git a/packages/testkit/tsconfig.esm.json b/packages/testkit/tsconfig.esm.json new file mode 100644 index 0000000000..d9877bd4bc --- /dev/null +++ b/packages/testkit/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "dist/esm", + "module": "esnext" + }, + "include": ["./**/*.ts"] + } \ No newline at end of file diff --git a/packages/testkit/tsconfig.json b/packages/testkit/tsconfig.json index 198d44ed7c..848b3eb15a 100644 --- a/packages/testkit/tsconfig.json +++ b/packages/testkit/tsconfig.json @@ -1,28 +1,20 @@ { - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "outDir": "dist", - "strict": true, - "sourceMap": true, - "strictNullChecks": true, - "moduleResolution": "node", - "importHelpers": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "rootDir": ".", - "declaration": false, - "downlevelIteration": true, - "experimentalDecorators": true, - "noImplicitOverride": true, - "noImplicitReturns": true, - }, - "include": [ "buttons", "inputs", "navigation", "pickers", "popover", "text", "utils", "BaseElement.ts", "index.ts"] - } + "compilerOptions": { + "lib": ["ESNext"], + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "strict": true, + "skipLibCheck": true, + "noImplicitAny": true, + "outDir": "dist", + "baseUrl": ".", + "allowSyntheticDefaultImports": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["./**/*.ts"], + "exclude": [ + "__tests__", "./playwright.config.ts", "node_modules", "dist"] +}