diff --git a/.babelrc b/.babelrc index 2c055d07..1f7e3f8b 100644 --- a/.babelrc +++ b/.babelrc @@ -11,6 +11,5 @@ ], "@babel/preset-typescript", "@babel/preset-react" - ], - "plugins": ["babel-plugin-styled-components"] + ] } diff --git a/.storybook/main.ts b/.storybook/main.ts index f5e4f5ff..474ae7e1 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -5,20 +5,26 @@ const config: StorybookConfig = { '../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)', ], - staticDirs: ['../src/fonts/'], + + // staticDirs: ['../src/fonts/'], + addons: [ '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-onboarding', '@storybook/addon-interactions', - '@storybook/addon-mdx-gfm', + '@chromatic-com/storybook', ], + framework: { name: '@storybook/react-vite', options: {}, }, - docs: { - autodocs: 'tag', + + docs: {}, + + typescript: { + reactDocgen: 'react-docgen-typescript', }, } export default config diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 1f3b073e..f4c014ac 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,11 +1,10 @@ import type { Preview } from '@storybook/react' import React from 'react' -import { ThemeProvider } from 'styled-components' +import '../src/tailwind.css' import theme from '../src/theme' const preview: Preview = { parameters: { - actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, @@ -13,13 +12,7 @@ const preview: Preview = { }, }, }, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [(Story) => ], } export default preview diff --git a/package.json b/package.json index 49d9f519..9f3e0b3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ooni-components", - "version": "0.6.0-alpha.11", + "version": "0.7.0-alpha.6", "main": "dist/index.cjs.js", "module": "dist/index.esm.js", "types": "dist/index.d.ts", @@ -9,10 +9,11 @@ "author": "Arturo Filastò ", "license": "BSD-3-Clause", "dependencies": { - "@styled-system/css": "^5.1.5", - "@styled-system/should-forward-prop": "^5.1.5", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "mini-svg-data-uri": "^1.4.4", "react-select": "^5.8.0", - "styled-system": "^5.1.5" + "tailwind-merge": "^2.3.0" }, "devDependencies": { "@babel/core": "^7.23.9", @@ -20,23 +21,23 @@ "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@biomejs/biome": "1.5.3", + "@chromatic-com/storybook": "^1.6.1", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", "@rollup/plugin-url": "^8.0.2", - "@storybook/addon-essentials": "^7.6.17", - "@storybook/addon-interactions": "^7.6.17", - "@storybook/addon-links": "^7.6.17", - "@storybook/addon-mdx-gfm": "^7.6.17", - "@storybook/addon-onboarding": "^1.0.11", - "@storybook/blocks": "^7.6.17", - "@storybook/react": "^7.6.17", - "@storybook/react-vite": "^7.6.17", - "@storybook/test": "^7.6.17", - "@storybook/testing-library": "^0.2.2", + "@storybook/addon-essentials": "^8.2.1", + "@storybook/addon-interactions": "^8.2.1", + "@storybook/addon-links": "^8.2.1", + "@storybook/addon-onboarding": "^8.2.1", + "@storybook/blocks": "^8.2.1", + "@storybook/react": "^8.2.1", + "@storybook/react-vite": "^8.2.1", + "@storybook/test": "^8.2.1", "@svgr/rollup": "^8.1.0", + "@tailwindcss/forms": "^0.5.7", "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.2.1", @@ -44,20 +45,17 @@ "@types/jest": "^29.5.12", "@types/react": "^18.2.57", "@types/react-dom": "^18.2.19", - "@types/styled-system": "^5.1.22", - "@types/styled-system__css": "^5.0.21", + "autoprefixer": "^10.4.17", "babel-plugin-inline-react-svg": "^2.0.2", - "babel-plugin-styled-components": "^2.1.4", "cheerio": "^1.0.0-rc.12", "gh-pages": "^6.1.1", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "jest-styled-components": "^7.1.1", "jest-svg-transformer": "^1.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-icons": "^5.0.1", - "rimraf": "^5.0.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-icons": "^5.2.1", + "rimraf": "^5.0.7", "rollup": "^4.12.0", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-delete": "^2.0.0", @@ -66,25 +64,22 @@ "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-typescript2": "^0.36.0", "rollup-plugin-visualizer": "^5.12.0", - "storybook": "^7.6.17", - "styled-components": "^6.1.8", - "ts-jest": "^29.1.2", - "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vite": "^5.1.3" - }, - "resolutions": { - "serialize-javascript": "^6.0.0", - "node-fetch": "^2.6.7", - "jackspeak": "2.1.1" + "storybook": "^8.2.1", + "tailwindcss": "^3.4.4", + "ts-jest": "^29.1.5", + "tslib": "^2.6.3", + "typescript": "^5.5.3", + "vite": "^5.3.3", + "yalc": "^1.0.0-pre.53" }, "peerDependencies": { "react": ">= 17", "react-icons": ">= 4", - "styled-components": ">= 6" + "tailwindcss": ">=3.0.0" }, "scripts": { "build": "yarn run rollup -c", + "build:tailwind": "tailwindcss -o tailwind/tailwind.css", "create-icons-dir": "rimraf src/components/icons && npx mkdirp src/components/icons", "create-icons": "npm run create-icons-dir && node src/bin/create-icons", "tag": "git tag -s -a v$npm_package_version -m \"ooni-components $npm_package_version\"", @@ -100,5 +95,5 @@ "storybook": "storybook dev -p 6006", "storybook:build": "storybook build" }, - "files": ["dist", "animations", "svgs", "index.d.ts", "icons"] + "files": ["dist", "animations", "svgs", "index.d.ts", "icons", "tailwind"] } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..825cb186 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,8 @@ +module.exports = { + plugins: { + 'postcss-import': {}, + 'tailwindcss/nesting': {}, + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/rollup.config.mjs b/rollup.config.mjs index af279c61..10652abd 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -53,7 +53,7 @@ const mainBundle = { copy({ targets: [ { - src: './src/fonts', + src: './src/tailwind.css', dest: './dist', }, ], diff --git a/src/__test__/Button.test.tsx b/src/__test__/Button.test.tsx deleted file mode 100644 index 035f8661..00000000 --- a/src/__test__/Button.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react' -import { screen } from '@testing-library/react' -import 'jest-styled-components' -import renderWithWrapper from './index' -import { Button } from '..' - -describe('Button', () => { - test('renders Button', () => { - renderWithWrapper({ component: - - ) -} - -export default FacebookShareButton diff --git a/src/components/Flex.tsx b/src/components/Flex.tsx deleted file mode 100644 index a84aff8a..00000000 --- a/src/components/Flex.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { FlexProps } from 'types' -import React from 'react' -import { styled } from 'styled-components' -import Box from './Box' - -const Flex = styled(Box)({ - display: 'flex', -}) - -export default Flex diff --git a/src/components/Heading.tsx b/src/components/Heading.tsx deleted file mode 100644 index 7ce86f96..00000000 --- a/src/components/Heading.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import { TextProps } from 'types' -import Text from './Text' - -export interface HeadingProps extends TextProps { - h?: number -} - -const Heading = ({ h = 1, ...rest }: HeadingProps) => { - const getHeadingElement = () => { - switch (h) { - case 1: - return 'h1' - case 2: - return 'h2' - case 3: - return 'h3' - case 4: - return 'h4' - case 5: - return 'h5' - default: - return 'h3' - } - } - - return ( - - ) -} - -export default Heading diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx deleted file mode 100644 index f8886feb..00000000 --- a/src/components/Hero.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react' -import { BoxProps } from 'types' -import Box from './Box' - -const Hero = (props: BoxProps) => { - return -} - -export default Hero diff --git a/src/components/HeroLead.tsx b/src/components/HeroLead.tsx deleted file mode 100644 index 52625ba7..00000000 --- a/src/components/HeroLead.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react' -import { TextProps } from 'types' -import Text from './Text' - -const HeroLead = (props: TextProps) => - -export default HeroLead diff --git a/src/components/IconButton.tsx b/src/components/IconButton.tsx index 8c45bd9b..ed41b4d2 100644 --- a/src/components/IconButton.tsx +++ b/src/components/IconButton.tsx @@ -1,16 +1,12 @@ import React, { ReactNode } from 'react' -import Button, { ButtonProps } from './Button' -export interface IconButtonProps extends ButtonProps { +export interface IconButtonProps + extends React.HtmlHTMLAttributes { icon: ReactNode } const IconButton = ({ icon, ...rest }: IconButtonProps) => { - return ( - - ) + return } export default IconButton diff --git a/src/components/Image.tsx b/src/components/Image.tsx deleted file mode 100644 index d8099e12..00000000 --- a/src/components/Image.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { forwardRef } from 'react' -import { ImageProps } from 'types' -import Box from './Box' - -const Image = forwardRef((props: ImageProps, ref) => ( - -)) - -export default Image diff --git a/src/components/Input.tsx b/src/components/Input.tsx index ecff186f..2db9ec88 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -1,45 +1,53 @@ import React, { forwardRef } from 'react' -import { InputProps as IP } from 'types' -import { getMarginProps, omitMarginProps } from '../utils' -import Box from './Box' import ErrorMessage from './ErrorMessage' -import Text from './Text' -export interface InputProps extends IP { - error?: string | undefined +type InputProps = Omit, 'type'> & { + error?: string label?: string } -const Input = forwardRef(({ error, name, label, ...rest }: InputProps, ref) => { - return ( - - {label && ( - - {label} - - )} - - {error && {error}} - - ) -}) +const Input = forwardRef( + ({ error, name, label, className, ...props }, ref) => { + return ( +
+ {label && ( + + )} + + {error && {error}} +
+ ) + }, +) export default Input diff --git a/src/components/InputWithIconButton.tsx b/src/components/InputWithIconButton.tsx deleted file mode 100644 index 7ef912cc..00000000 --- a/src/components/InputWithIconButton.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { ReactNode } from 'react' -import { InputProps } from 'types' -import Box from './Box' -import Flex from './Flex' -import IconButton from './IconButton' -import Input from './Input' - -interface InputWithIconButtonProps extends InputProps { - onAction?: () => void - icon?: ReactNode -} - -export const InputWithIconButton = ({ - onAction, - icon, - ...rest -}: InputWithIconButtonProps) => { - return ( - - - - - {icon ? ( - - - - ) : null} - - ) -} - -export default InputWithIconButton diff --git a/src/components/Label.tsx b/src/components/Label.tsx deleted file mode 100644 index 30a4295f..00000000 --- a/src/components/Label.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { forwardRef } from 'react' -import { LabelProps } from 'types' -import Text from './Flex' - -const Label = forwardRef((props: LabelProps, ref) => ( - -)) - -export default Label diff --git a/src/components/Link.tsx b/src/components/Link.tsx deleted file mode 100644 index 72cfb8fe..00000000 --- a/src/components/Link.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React, { forwardRef } from 'react' -import { LinkProps } from 'types' -import Box from './Box' - -const Link = forwardRef((props: LinkProps, ref) => ( - -)) - -export default Link diff --git a/src/components/LogoOONIRun.tsx b/src/components/LogoOONIRun.tsx index c923e866..8e63fadb 100644 --- a/src/components/LogoOONIRun.tsx +++ b/src/components/LogoOONIRun.tsx @@ -1,9 +1,13 @@ import React from 'react' import OONIRunLogo from '../../svgs/logos/Run-HorizontalMonochromeInverted.svg' -import Image from './Image' const LogoOONIRun = ({ ...props }) => ( - + // biome-ignore lint/a11y/useAltText: + ) export default LogoOONIRun diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 404de6e0..001836de 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,9 +1,6 @@ import React, { ReactNode } from 'react' import { MdClose } from 'react-icons/md' -import { BoxProps } from 'types' -import Box from './Box' -import IconButton from './IconButton' -import Flex from './Flex' +import { twMerge } from 'tailwind-merge' interface ModalCloseButtonProps { icon: ReactNode @@ -11,79 +8,57 @@ interface ModalCloseButtonProps { } const ModalCloseButton = ({ icon, onClick }: ModalCloseButtonProps) => ( - - - +
+ +
) -export interface ModalProps extends BoxProps { +export interface ModalProps { show?: boolean onHideClick?: () => void + children: ReactNode + className?: string } export const Modal = ({ show = false, onHideClick, children, - sx, + className, ...rest }: ModalProps) => { return ( - - +
} /> {children} - +
{show && ( - )} - + ) } diff --git a/src/components/MultiSelect.tsx b/src/components/MultiSelect.tsx index f80adfc6..b22e20a2 100644 --- a/src/components/MultiSelect.tsx +++ b/src/components/MultiSelect.tsx @@ -1,74 +1,79 @@ -import React, { forwardRef } from 'react' +import React from 'react' import Select from 'react-select' -import { SelectProps } from 'types' -import theme from '../theme' -import { getMarginProps, omitMarginProps } from '../utils' -import Box from './Box' -import Text from './Text' +import { twMerge } from 'tailwind-merge' +import ErrorMessage from './ErrorMessage' -export interface MultiSelectProps extends SelectProps { +export interface MultiSelectProps { label?: string options: { label: string; value: string | number }[] + name: string + className?: string + error?: string } -const MultiSelect = forwardRef( - ({ label, name, options, ...rest }: MultiSelectProps, ref) => ( - - {label && ( - - {label} - - )} - ({ + outline: '0', + transition: 'all 100ms', + borderStyle: 'solid', + boxSizing: 'border-box', + borderRadius: '32px', + minHeight: '36.5px', + boxShadow: 'none', + }), + indicatorSeparator: () => ({ + display: 'none', + }), + dropdownIndicator: (baseStyles) => ({ + ...baseStyles, + padding: '7px', + }), + multiValue: (baseStyles) => ({ + ...baseStyles, + borderRadius: '20px', + }), + multiValueRemove: () => ({ + svg: { display: 'none' }, + '&:before': { + content: '"✕"', + fontSize: '80%', + padding: '0 8px 0 4px', + }, + }), + }} + classNames={{ + control: (state) => + twMerge( + 'flex flex-wrap cursor-default items-center relative border', + state.isFocused + ? 'border-blue-500 hover:border-blue-500' + : 'border-gray-600 hover:border-gray-800', + ), + multiValue: () => 'bg-gray-300', + multiValueRemove: () => + 'hover:cursor-pointer hover:text-red-700 self-center', + }} + {...props} + /> + {error && {error}} + ) export default MultiSelect diff --git a/src/components/MultiSelectCreatable.tsx b/src/components/MultiSelectCreatable.tsx index 9a4dd430..58b0d7e4 100644 --- a/src/components/MultiSelectCreatable.tsx +++ b/src/components/MultiSelectCreatable.tsx @@ -1,31 +1,23 @@ -import React, { - FocusEvent, - forwardRef, - KeyboardEventHandler, - useEffect, -} from 'react' +import React, { FocusEvent, KeyboardEventHandler, useEffect } from 'react' import { MultiValue } from 'react-select' import CreatableSelect from 'react-select/creatable' -import { SelectProps as SP } from 'types' -import theme from '../theme' -import { getMarginProps, omitMarginProps } from '../utils' -import Box from './Box' +import { twMerge } from 'tailwind-merge' import ErrorMessage from './ErrorMessage' -import Text from './Text' interface Option { readonly label: string readonly value: string } -export interface MultiSelectCreatableProps - extends Omit { +export interface MultiSelectCreatableProps { + name: string label?: string placeholder?: string value: MultiValue