diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 7f4c50a2..4850f282 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,17 +1,22 @@ import type { Preview, StoryContext, StoryFn } from '@storybook/react'; import type { ReactElement } from 'react'; +import { MantineProvider } from '@mantine/core'; + import { Provider, themes } from '@smile/react-front-kit-shared'; function withProvider(Story: StoryFn, context: StoryContext): ReactElement { return ( - - + + + + ); } diff --git a/apps/next/app/layout.tsx b/apps/next/app/layout.tsx index ef9db81f..8c6467fe 100644 --- a/apps/next/app/layout.tsx +++ b/apps/next/app/layout.tsx @@ -1,6 +1,6 @@ import type { ReactElement, ReactNode } from 'react'; -import { Provider, mainTheme } from '@smile/react-front-kit'; +import { Provider } from '@smile/react-front-kit-shared'; import { Open_Sans } from 'next/font/google'; const openSans = Open_Sans({ @@ -18,9 +18,7 @@ export default function RootLayout(props: IRootLayoutProps): ReactElement { return ( - - {children} - + {children} ); diff --git a/apps/next/package.json b/apps/next/package.json index 6af88c21..6acd83ef 100644 --- a/apps/next/package.json +++ b/apps/next/package.json @@ -17,6 +17,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "@smile/react-front-kit": "*", + "@smile/react-front-kit-shared": "*", "storybook-pages": "*" }, "devDependencies": { @@ -28,6 +29,6 @@ "typescript": "~5.2.0" }, "lint-staged": { - "*.{js,jsx,ts,tsx}": "eslint --max-warnings=0 --ignore-path ../../.gitignore" + "*.{js,jsx,ts,tsx}": "eslint --ignore-path ../../.gitignore" } } diff --git a/package-lock.json b/package-lock.json index f7364854..319b1ffd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,6 +58,7 @@ "@mantine/styles": "^6.0.20", "@phosphor-icons/react": "^2.0.10", "@smile/react-front-kit": "*", + "@smile/react-front-kit-shared": "*", "next": "^13.4.19", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -31430,10 +31431,10 @@ }, "packages/react-front-kit": { "name": "@smile/react-front-kit", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "dependencies": { - "@smile/react-front-kit-shared": "0.5.0", + "@smile/react-front-kit-shared": "0.6.0", "pretty-bytes": "^6.1.1" }, "devDependencies": { @@ -31469,11 +31470,11 @@ }, "packages/react-front-kit-dropzone": { "name": "@smile/react-front-kit-dropzone", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "dependencies": { - "@smile/react-front-kit": "0.5.0", - "@smile/react-front-kit-shared": "0.5.0" + "@smile/react-front-kit": "0.6.0", + "@smile/react-front-kit-shared": "0.6.0" }, "devDependencies": { "@babel/preset-env": "^7.22.20", @@ -32551,7 +32552,7 @@ }, "packages/react-front-kit-shared": { "name": "@smile/react-front-kit-shared", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "devDependencies": { "@babel/preset-env": "^7.22.20", @@ -33627,11 +33628,11 @@ }, "packages/react-front-kit-table": { "name": "@smile/react-front-kit-table", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "dependencies": { - "@smile/react-front-kit": "0.5.0", - "@smile/react-front-kit-shared": "0.5.0" + "@smile/react-front-kit": "0.6.0", + "@smile/react-front-kit-shared": "0.6.0" }, "devDependencies": { "@babel/preset-env": "^7.22.20", @@ -35753,7 +35754,7 @@ } }, "packages/storybook-pages": { - "version": "0.1.0", + "version": "0.6.0", "license": "MIT", "dependencies": { "@smile/react-front-kit": "*", diff --git a/package.json b/package.json index 6b8b76a1..87fa18dd 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,11 @@ "build:app": "turbo run build:app", "dev": "turbo run dev", "lint": "turbo run lint && npm run lint:root", - "lint:root": "DEBUG=eslint:cli-engine eslint --max-warnings=0 --ignore-path <(cat .gitignore .eslintignore) . && tsc --noEmit", - "lint:fix": "DEBUG=eslint:cli-engine eslint --fix --max-warnings=0 --ignore-path <(cat .gitignore .eslintignore) . && tsc --noEmit", + "lint:root": "DEBUG=eslint:cli-engine eslint --ignore-path <(cat .gitignore .eslintignore) . && tsc --noEmit", + "lint:fix": "DEBUG=eslint:cli-engine eslint --fix --ignore-path <(cat .gitignore .eslintignore) . && tsc --noEmit", "test": "turbo run test", "format": "prettier --write --ignore-unknown --ignore-path .prettierignore --ignore-path .gitignore .", + "generate": "turbo gen react-component", "changeset": "changeset", "version": "turbo run build lint test && changeset version", "publish": "turbo run build lint test && changeset publish", @@ -88,6 +89,6 @@ }, "lint-staged": { "*": "prettier --write --ignore-unknown --ignore-path <(cat .gitignore .prettierignore)", - "*.{js,jsx,ts,tsx}": "eslint --max-warnings=0 --ignore-path <(cat .gitignore .eslintignore)" + "*.{js,jsx,ts,tsx}": "eslint --ignore-path <(cat .gitignore .eslintignore)" } } diff --git a/packages/react-front-kit-dropzone/CHANGELOG.md b/packages/react-front-kit-dropzone/CHANGELOG.md index 0e52790b..f96e66bb 100644 --- a/packages/react-front-kit-dropzone/CHANGELOG.md +++ b/packages/react-front-kit-dropzone/CHANGELOG.md @@ -1,5 +1,17 @@ # @smile/react-front-kit-dropzone +## 0.7.0 + +### Minor Changes + +- [#87](https://github.com/Smile-SA/react-front-kit/pull/87) [`dbc06ca`](https://github.com/Smile-SA/react-front-kit/commit/dbc06ca55961b69663ab7fdc02609c6525ae361d) Thanks [@tonai](https://github.com/tonai)! - Add theme provider and update barrel file for better next.js compatibility + +### Patch Changes + +- Updated dependencies [[`dbc06ca`](https://github.com/Smile-SA/react-front-kit/commit/dbc06ca55961b69663ab7fdc02609c6525ae361d), [`7415d9b`](https://github.com/Smile-SA/react-front-kit/commit/7415d9b9d119abfc850dda2ab6fa94845e72aee6), [`5696400`](https://github.com/Smile-SA/react-front-kit/commit/5696400e6f703da52db5f7199b50f8251fa76726), [`eede698`](https://github.com/Smile-SA/react-front-kit/commit/eede698ce172e20eb1de4c70e3d59b7510afb9df), [`480af11`](https://github.com/Smile-SA/react-front-kit/commit/480af1122b41e28d938bd665c1aa998272c99d9a), [`70b674f`](https://github.com/Smile-SA/react-front-kit/commit/70b674f3513b4bf996e8f83a46c8a132ca3712ac)]: + - @smile/react-front-kit-shared@0.7.0 + - @smile/react-front-kit@0.7.0 + ## 0.6.0 ### Patch Changes diff --git a/packages/react-front-kit-dropzone/package.json b/packages/react-front-kit-dropzone/package.json index 573285b4..ba13a361 100644 --- a/packages/react-front-kit-dropzone/package.json +++ b/packages/react-front-kit-dropzone/package.json @@ -1,6 +1,6 @@ { "name": "@smile/react-front-kit-dropzone", - "version": "0.6.0", + "version": "0.7.0", "description": "Dropzone React component library based on mantine", "license": "MIT", "homepage": "https://github.com/Smile-SA/react-front-kit", @@ -47,8 +47,8 @@ "prepublishOnly": "npm run build && node ../../scripts/prepublish.mjs" }, "dependencies": { - "@smile/react-front-kit": "0.6.0", - "@smile/react-front-kit-shared": "0.6.0" + "@smile/react-front-kit": "0.7.0", + "@smile/react-front-kit-shared": "0.7.0" }, "devDependencies": { "@babel/preset-env": "^7.22.20", diff --git a/packages/react-front-kit-dropzone/src/index.tsx b/packages/react-front-kit-dropzone/src/index.tsx index 52022390..ac5805aa 100644 --- a/packages/react-front-kit-dropzone/src/index.tsx +++ b/packages/react-front-kit-dropzone/src/index.tsx @@ -1,2 +1,7 @@ /* eslint-disable react-refresh/only-export-components */ -export * from './Components/Dropzone/Dropzone'; + +'use client'; + +// component exports +export { Dropzone } from './Components/Dropzone/Dropzone'; +export type { IDropzoneProps, IFile } from './Components/Dropzone/Dropzone'; diff --git a/packages/react-front-kit-shared/CHANGELOG.md b/packages/react-front-kit-shared/CHANGELOG.md index f74a1012..28918fd5 100644 --- a/packages/react-front-kit-shared/CHANGELOG.md +++ b/packages/react-front-kit-shared/CHANGELOG.md @@ -1,5 +1,17 @@ # @smile/react-front-kit-shared +## 0.7.0 + +### Minor Changes + +- [#87](https://github.com/Smile-SA/react-front-kit/pull/87) [`dbc06ca`](https://github.com/Smile-SA/react-front-kit/commit/dbc06ca55961b69663ab7fdc02609c6525ae361d) Thanks [@tonai](https://github.com/tonai)! - Add theme provider and update barrel file for better next.js compatibility + +- [#95](https://github.com/Smile-SA/react-front-kit/pull/95) [`5696400`](https://github.com/Smile-SA/react-front-kit/commit/5696400e6f703da52db5f7199b50f8251fa76726) Thanks [@QuentinLeCaignec](https://github.com/QuentinLeCaignec)! - Added `FilterList` component, renamed `FiltersBar` in `SidebarFilters` + and `FiltersCheckboxList` in `SearchableCheckboxList`, + refactored `SearchableCheckboxList`, added common `IFilter` type in shared + package, added topContent prop to `SwitchableView`, fixed `BrowsingPage` with + name changes. + ## 0.6.0 ### Minor Changes diff --git a/packages/react-front-kit-shared/package.json b/packages/react-front-kit-shared/package.json index f29570f0..1ff93057 100644 --- a/packages/react-front-kit-shared/package.json +++ b/packages/react-front-kit-shared/package.json @@ -1,6 +1,6 @@ { "name": "@smile/react-front-kit-shared", - "version": "0.6.0", + "version": "0.7.0", "description": "Shared elements for react-front-kit component library based on mantine", "license": "MIT", "homepage": "https://github.com/Smile-SA/react-front-kit", diff --git a/packages/react-front-kit-shared/src/3-custom/Provider/Provider.tsx b/packages/react-front-kit-shared/src/3-custom/Provider/Provider.tsx index 47b6f9a2..8ecbff9e 100644 --- a/packages/react-front-kit-shared/src/3-custom/Provider/Provider.tsx +++ b/packages/react-front-kit-shared/src/3-custom/Provider/Provider.tsx @@ -1,27 +1,31 @@ 'use client'; -import type { MantineThemeOverride } from '@mantine/core'; +import type { IThemeOverride } from '../../types'; import type { ReactElement, ReactNode } from 'react'; import { MantineProvider } from '@mantine/core'; -import { mainTheme } from '../../theme'; +import { themeContext } from '../../contexts'; +import { createThemes } from '../../helpers'; -export interface IProviderProps { +export interface IProviderProps extends IThemeOverride { children?: ReactNode; colorScheme?: 'dark' | 'light'; - theme?: MantineThemeOverride; } export function Provider(props: IProviderProps): ReactElement { - const { children, colorScheme = 'light', theme = mainTheme } = props; + const { children, colorScheme = 'light', ...themeConfig } = props; + const themes = createThemes(themeConfig); + const { main } = themes; return ( - - {children} - + + + {children} + + ); } diff --git a/packages/react-front-kit-shared/src/contexts/index.ts b/packages/react-front-kit-shared/src/contexts/index.ts new file mode 100644 index 00000000..7b1f54ec --- /dev/null +++ b/packages/react-front-kit-shared/src/contexts/index.ts @@ -0,0 +1 @@ +export * from './theme'; diff --git a/packages/react-front-kit-shared/src/contexts/theme.ts b/packages/react-front-kit-shared/src/contexts/theme.ts new file mode 100644 index 00000000..487095f2 --- /dev/null +++ b/packages/react-front-kit-shared/src/contexts/theme.ts @@ -0,0 +1,29 @@ +import type { IThemes } from '../types'; +import type { MantineThemeOverride } from '@mantine/styles'; + +import { createContext, useContext } from 'react'; + +export const themeContext = createContext({ + main: {}, + primary: {}, + secondary: {}, +}); + +export function useThemes(): IThemes { + return useContext(themeContext); +} + +export function useMainTheme(): MantineThemeOverride { + const { main } = useContext(themeContext); + return main; +} + +export function usePrimaryTheme(): MantineThemeOverride { + const { primary } = useContext(themeContext); + return primary; +} + +export function useSecondaryTheme(): MantineThemeOverride { + const { secondary } = useContext(themeContext); + return secondary; +} diff --git a/packages/react-front-kit-shared/src/helpers/index.ts b/packages/react-front-kit-shared/src/helpers/index.ts index a7f9b21d..7e2867ed 100644 --- a/packages/react-front-kit-shared/src/helpers/index.ts +++ b/packages/react-front-kit-shared/src/helpers/index.ts @@ -1,2 +1,3 @@ -export * from './utilities'; +export * from './theme'; export * from './typeGuard'; +export * from './utilities'; diff --git a/packages/react-front-kit-shared/src/helpers/theme.ts b/packages/react-front-kit-shared/src/helpers/theme.ts new file mode 100644 index 00000000..fb28e84e --- /dev/null +++ b/packages/react-front-kit-shared/src/helpers/theme.ts @@ -0,0 +1,174 @@ +import type { IThemeOverride, IThemes } from '../types'; +import type { MantineThemeOverride } from '@mantine/core'; + +import { DEFAULT_THEME, rem } from '@mantine/core'; + +export function createThemes(themeConfig: IThemeOverride = {}): IThemes { + const { + baseTheme = {}, + mainTheme = {}, + primaryColor = 'cyan', + primaryTheme = {}, + secondaryColor = 'gray', + secondaryTheme = {}, + } = themeConfig; + + const { + components: baseComponents, + focusRingStyles: baseFocusRingStyles, + fontSizes: baseFontSizes, + headings: baseHeadings, + ...baseRest + } = baseTheme; + const base: MantineThemeOverride = { + activeStyles: { + transform: '', + }, + components: { + Checkbox: { + defaultProps: { + radius: 'sm', + }, + }, + ...baseComponents, + }, + cursorType: 'pointer', + defaultRadius: '1.5rem', + focusRingStyles: { + // Default orange for focus-ring on all elements and Input-based components + inputStyles: (theme) => ({ + outline: `${rem(2)} solid ${theme.colors.orange[5]}`, + }), + styles: (theme) => ({ + outline: `${rem(2)} solid ${theme.colors.orange[5]}`, + }), + ...baseFocusRingStyles, + }, + fontFamily: 'var(--rfk-font, Open Sans)', + fontSizes: { + lg: '18px', + md: '14px', + sm: '12px', + xl: '20px', + xs: '10px', + ...baseFontSizes, + }, + headings: { + fontWeight: 700, + sizes: { + h1: { fontSize: '26px', lineHeight: '135%' }, + h2: { fontSize: '18px', lineHeight: '155%' }, + h3: { fontSize: '14px', lineHeight: '155%' }, + }, + ...baseHeadings, + }, + ...baseRest, + }; + + const { globalStyles: mainGlobalStyles, ...mainRest } = mainTheme; + const main: MantineThemeOverride = { + ...base, + black: DEFAULT_THEME.colors.dark[6], + colorScheme: 'light', + globalStyles: (theme) => ({ + a: { + color: 'inherit', + }, + body: { + background: + theme.colorScheme === 'light' + ? theme.colors.gray[1] + : theme.colors.gray[9], + }, + ...mainGlobalStyles, + }), + primaryColor, + primaryShade: 9, + white: '#fff', + ...mainRest, + }; + + const { + colors: primaryColors, + components: primaryComponents, + ...primaryRest + } = primaryTheme; + const primary: MantineThemeOverride = { + ...base, + black: DEFAULT_THEME.colors[primaryColor][9], + colorScheme: 'dark', + colors: { + dark: [ + '#fff', + DEFAULT_THEME.colors[primaryColor][1], + DEFAULT_THEME.colors[primaryColor][7], + DEFAULT_THEME.colors[primaryColor][7], + DEFAULT_THEME.colors[primaryColor][7], + DEFAULT_THEME.colors[primaryColor][8], + DEFAULT_THEME.colors[primaryColor][8], + DEFAULT_THEME.colors[primaryColor][9], + DEFAULT_THEME.colors[primaryColor][9], + DEFAULT_THEME.colors[primaryColor][9], + ], + ...primaryColors, + }, + components: { + ...base.components, + Input: { + defaultProps: { + variant: 'filled', + }, + }, + ...primaryComponents, + }, + primaryColor: 'dark', + primaryShade: 0, + white: '#000', + ...primaryRest, + }; + + const { + colors: secondaryColors, + components: secondaryComponents, + ...secondaryRest + } = secondaryTheme; + const secondary: MantineThemeOverride = { + ...base, + black: DEFAULT_THEME.colors[secondaryColor][8], + colorScheme: 'dark', + colors: { + dark: [ + '#fff', + DEFAULT_THEME.colors[secondaryColor][1], + DEFAULT_THEME.colors[secondaryColor][6], + DEFAULT_THEME.colors[secondaryColor][6], + DEFAULT_THEME.colors[secondaryColor][6], + DEFAULT_THEME.colors[secondaryColor][7], + DEFAULT_THEME.colors[secondaryColor][7], + DEFAULT_THEME.colors[secondaryColor][8], + DEFAULT_THEME.colors[secondaryColor][9], + DEFAULT_THEME.colors[secondaryColor][9], + ], + ...secondaryColors, + }, + components: { + ...base.components, + Input: { + defaultProps: { + variant: 'filled', + }, + }, + ...secondaryComponents, + }, + primaryColor: 'dark', + primaryShade: 0, + white: '#000', + ...secondaryRest, + }; + + return { + main, + primary, + secondary, + }; +} diff --git a/packages/react-front-kit-shared/src/index.tsx b/packages/react-front-kit-shared/src/index.tsx index 3b4cd4be..f51df466 100644 --- a/packages/react-front-kit-shared/src/index.tsx +++ b/packages/react-front-kit-shared/src/index.tsx @@ -1,7 +1,45 @@ /* eslint-disable react-refresh/only-export-components */ -export * from './1-styleGuide/Icons'; -export * from './1-styleGuide/FileIcon/FileIcon'; -export * from './3-custom/Provider/Provider'; -export * from './helpers'; -export * from './types'; -export * from './theme'; +'use client'; + +// component exports +export { ColumnPlus, FolderMove } from './1-styleGuide/Icons'; +export { FileIcon } from './1-styleGuide/FileIcon/FileIcon'; +export type { IFileIconProps } from './1-styleGuide/FileIcon/FileIcon'; +export { Provider } from './3-custom/Provider/Provider'; +export type { IProviderProps } from './3-custom/Provider/Provider'; +// context exports +export { + themeContext, + useMainTheme, + usePrimaryTheme, + useSecondaryTheme, + useThemes, +} from './contexts'; +// helper exports +export { + createThemes, + isCallback, + isNotNullNorEmpty, + typeGuard, + typeGuardInterface, +} from './helpers'; +export type { + IGuardedType, + IPrimitiveOrConstructor, + ITypeGenericInterface, + ITypeMap, +} from './helpers'; +// type exports +export type { + IAction, + IActionConfirmModalProps, + IConfirmAction, + IConfirmModal, + IOption, + IOptions, + IThemeOverride, + IThemes, + IFilter, +} from './types'; +// type exports +export { mainTheme, primaryTheme, secondaryTheme, themes } from './theme'; diff --git a/packages/react-front-kit-shared/src/theme.ts b/packages/react-front-kit-shared/src/theme.ts index f6de92a3..1ff91849 100644 --- a/packages/react-front-kit-shared/src/theme.ts +++ b/packages/react-front-kit-shared/src/theme.ts @@ -1,145 +1,28 @@ 'use client'; -import type { MantineThemeOverride } from '@mantine/core'; +import { DEFAULT_THEME } from '@mantine/core'; -import { DEFAULT_THEME, rem } from '@mantine/core'; +import { createThemes } from './helpers'; -export const baseTheme: MantineThemeOverride = { - activeStyles: { - transform: '', - }, - components: { - Checkbox: { - defaultProps: { - radius: 'sm', - }, - }, - }, - cursorType: 'pointer', - defaultRadius: '1.5rem', - focusRingStyles: { - // Default orange for focus-ring on all elements and Input-based components - inputStyles: (theme) => ({ - outline: `${rem(2)} solid ${theme.colors.orange[5]}`, - }), - styles: (theme) => ({ - outline: `${rem(2)} solid ${theme.colors.orange[5]}`, - }), - }, - fontFamily: 'var(--rfk-font, Open Sans)', - fontSizes: { - lg: '18px', - md: '14px', - sm: '12px', - xl: '20px', - xs: '10px', - }, - headings: { - fontWeight: 700, - sizes: { - h1: { fontSize: '26px', lineHeight: '135%' }, - h2: { fontSize: '18px', lineHeight: '155%' }, - h3: { fontSize: '14px', lineHeight: '155%' }, - }, - }, -}; - -export const mainTheme: MantineThemeOverride = { - ...baseTheme, - black: DEFAULT_THEME.colors.dark[6], - colorScheme: 'light', - globalStyles: (theme) => ({ - a: { - color: 'inherit', - }, - body: { - background: - theme.colorScheme === 'light' - ? theme.colors.gray[1] - : theme.colors.gray[9], - }, - }), - primaryColor: 'cyan', - primaryShade: 9, - white: '#fff', -}; - -export const primaryTheme: MantineThemeOverride = { - ...baseTheme, - black: DEFAULT_THEME.colors.cyan[9], - colorScheme: 'dark', - colors: { - dark: [ - '#fff', - DEFAULT_THEME.colors.cyan[1], - DEFAULT_THEME.colors.cyan[7], - DEFAULT_THEME.colors.cyan[7], - DEFAULT_THEME.colors.cyan[7], - DEFAULT_THEME.colors.cyan[8], - DEFAULT_THEME.colors.cyan[8], - DEFAULT_THEME.colors.cyan[9], - DEFAULT_THEME.colors.cyan[9], - DEFAULT_THEME.colors.cyan[9], - ], - }, - components: { - ...baseTheme.components, - Input: { - defaultProps: { - variant: 'filled', - }, - }, - }, - primaryColor: 'dark', - primaryShade: 0, - white: '#000', -}; - -export const secondaryTheme: MantineThemeOverride = { - ...baseTheme, - black: DEFAULT_THEME.colors.gray[8], - colorScheme: 'dark', - colors: { - dark: [ - '#fff', - DEFAULT_THEME.colors.gray[1], - DEFAULT_THEME.colors.gray[6], - DEFAULT_THEME.colors.gray[6], - DEFAULT_THEME.colors.gray[6], - DEFAULT_THEME.colors.gray[7], - DEFAULT_THEME.colors.gray[7], - DEFAULT_THEME.colors.gray[8], - DEFAULT_THEME.colors.gray[9], - DEFAULT_THEME.colors.gray[9], - ], - }, - components: { - ...baseTheme.components, - Input: { - defaultProps: { - variant: 'filled', - }, - }, - }, - primaryColor: 'dark', - primaryShade: 0, - white: '#000', -}; +const { main, primary, secondary } = createThemes(); +export const mainTheme = main; +export const primaryTheme = primary; +export const secondaryTheme = secondary; export const themes = { main: { color: '#fff', - theme: mainTheme, + theme: main, title: 'Main theme', }, primary: { color: DEFAULT_THEME.colors.cyan[9], - theme: primaryTheme, + theme: primary, title: 'Primary theme', }, secondary: { color: DEFAULT_THEME.colors.gray[8], - theme: secondaryTheme, + theme: secondary, title: 'Secondary theme', }, }; diff --git a/packages/react-front-kit-shared/src/types/actions.ts b/packages/react-front-kit-shared/src/types/actions.ts index 964c96af..3fc7c44b 100644 --- a/packages/react-front-kit-shared/src/types/actions.ts +++ b/packages/react-front-kit-shared/src/types/actions.ts @@ -30,7 +30,7 @@ export interface IAction { isItemAction?: boolean; isMassAction?: boolean; label: string | ((item: Item) => string); - onAction?: (item: Item | Item[]) => void; + onAction?: (item: Item) => void; } export interface IConfirmAction extends IAction { diff --git a/packages/react-front-kit-shared/src/types/filters.ts b/packages/react-front-kit-shared/src/types/filters.ts new file mode 100644 index 00000000..31493a74 --- /dev/null +++ b/packages/react-front-kit-shared/src/types/filters.ts @@ -0,0 +1,8 @@ +import type { ReactElement } from 'react'; + +export interface IFilter { + active?: boolean; + component?: ReactElement; + id: number | string; + label: string; +} diff --git a/packages/react-front-kit-shared/src/types/index.ts b/packages/react-front-kit-shared/src/types/index.ts index b914401a..79483127 100644 --- a/packages/react-front-kit-shared/src/types/index.ts +++ b/packages/react-front-kit-shared/src/types/index.ts @@ -1,2 +1,4 @@ export * from './actions'; export * from './options'; +export * from './theme'; +export * from './filters'; diff --git a/packages/react-front-kit-shared/src/types/theme.ts b/packages/react-front-kit-shared/src/types/theme.ts new file mode 100644 index 00000000..baf38689 --- /dev/null +++ b/packages/react-front-kit-shared/src/types/theme.ts @@ -0,0 +1,16 @@ +import type { MantineThemeColors, MantineThemeOverride } from '@mantine/core'; + +export interface IThemeOverride { + baseTheme?: MantineThemeOverride; + mainTheme?: MantineThemeOverride; + primaryColor?: keyof MantineThemeColors; + primaryTheme?: MantineThemeOverride; + secondaryColor?: keyof MantineThemeColors; + secondaryTheme?: MantineThemeOverride; +} + +export interface IThemes { + main: MantineThemeOverride; + primary: MantineThemeOverride; + secondary: MantineThemeOverride; +} diff --git a/packages/react-front-kit-table/CHANGELOG.md b/packages/react-front-kit-table/CHANGELOG.md index 825f8ca9..3a616216 100644 --- a/packages/react-front-kit-table/CHANGELOG.md +++ b/packages/react-front-kit-table/CHANGELOG.md @@ -1,5 +1,17 @@ # @smile/react-front-kit-table +## 0.7.0 + +### Minor Changes + +- [#87](https://github.com/Smile-SA/react-front-kit/pull/87) [`dbc06ca`](https://github.com/Smile-SA/react-front-kit/commit/dbc06ca55961b69663ab7fdc02609c6525ae361d) Thanks [@tonai](https://github.com/tonai)! - Add theme provider and update barrel file for better next.js compatibility + +### Patch Changes + +- Updated dependencies [[`dbc06ca`](https://github.com/Smile-SA/react-front-kit/commit/dbc06ca55961b69663ab7fdc02609c6525ae361d), [`7415d9b`](https://github.com/Smile-SA/react-front-kit/commit/7415d9b9d119abfc850dda2ab6fa94845e72aee6), [`5696400`](https://github.com/Smile-SA/react-front-kit/commit/5696400e6f703da52db5f7199b50f8251fa76726), [`eede698`](https://github.com/Smile-SA/react-front-kit/commit/eede698ce172e20eb1de4c70e3d59b7510afb9df), [`480af11`](https://github.com/Smile-SA/react-front-kit/commit/480af1122b41e28d938bd665c1aa998272c99d9a), [`70b674f`](https://github.com/Smile-SA/react-front-kit/commit/70b674f3513b4bf996e8f83a46c8a132ca3712ac)]: + - @smile/react-front-kit-shared@0.7.0 + - @smile/react-front-kit@0.7.0 + ## 0.6.0 ### Minor Changes diff --git a/packages/react-front-kit-table/package.json b/packages/react-front-kit-table/package.json index e0fa25ff..3771af4b 100644 --- a/packages/react-front-kit-table/package.json +++ b/packages/react-front-kit-table/package.json @@ -1,6 +1,6 @@ { "name": "@smile/react-front-kit-table", - "version": "0.6.0", + "version": "0.7.0", "description": "Table React component library based on mantine", "license": "MIT", "homepage": "https://github.com/Smile-SA/react-front-kit", @@ -61,8 +61,8 @@ "prepublishOnly": "npm run build && node ../../scripts/prepublish.mjs" }, "dependencies": { - "@smile/react-front-kit": "0.6.0", - "@smile/react-front-kit-shared": "0.6.0" + "@smile/react-front-kit": "0.7.0", + "@smile/react-front-kit-shared": "0.7.0" }, "devDependencies": { "@babel/preset-env": "^7.22.20", diff --git a/packages/react-front-kit-table/src/Components/Table/Table.style.tsx b/packages/react-front-kit-table/src/Components/Table/Table.style.tsx index d81f30af..02ca1f87 100644 --- a/packages/react-front-kit-table/src/Components/Table/Table.style.tsx +++ b/packages/react-front-kit-table/src/Components/Table/Table.style.tsx @@ -2,7 +2,7 @@ import { createStyles, getStylesRef } from '@mantine/styles'; export const useStyles = createStyles((theme) => ({ alertBanner: { - background: theme.colors.cyan[9], + background: theme.fn.primaryColor(), border: 0, borderRadius: 0, height: '56px', @@ -19,7 +19,7 @@ export const useStyles = createStyles((theme) => ({ '& svg': { filter: 'contrast(8) invert(1)', }, - backgroundColor: theme.colors.cyan[9], + backgroundColor: theme.fn.primaryColor(), borderRadius: '4px', display: 'flex', height: '28px', diff --git a/packages/react-front-kit-table/src/Components/Table/Table.tsx b/packages/react-front-kit-table/src/Components/Table/Table.tsx index a7b876d8..c8c3585d 100644 --- a/packages/react-front-kit-table/src/Components/Table/Table.tsx +++ b/packages/react-front-kit-table/src/Components/Table/Table.tsx @@ -19,7 +19,8 @@ import { DotsThreeVertical, Funnel, } from '@phosphor-icons/react'; -import { ColumnPlus, ConfirmModal, Pagination } from '@smile/react-front-kit'; +import { ConfirmModal, Pagination } from '@smile/react-front-kit'; +import { ColumnPlus } from '@smile/react-front-kit-shared'; import { MantineReactTable, MRT_ShowHideColumnsButton as MrtShowHideColumnsButton, diff --git a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx index d59697c7..2f9ac6c3 100644 --- a/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx +++ b/packages/react-front-kit-table/src/Components/TableGridView/TableGridView.tsx @@ -2,13 +2,13 @@ import type { ITableData } from '../../types'; import type { ITableProps } from '../Table/Table'; import type { Record } from '@phosphor-icons/react'; import type { - IAction, IDataView, ISwitchableViewProps, IThumbnail, IThumbnailData, IThumbnailGridProps, } from '@smile/react-front-kit'; +import type { IAction } from '@smile/react-front-kit-shared'; import type { MRT_RowSelectionState } from 'mantine-react-table'; import type { ReactElement, SetStateAction } from 'react'; diff --git a/packages/react-front-kit-table/src/index.tsx b/packages/react-front-kit-table/src/index.tsx index 2f898cd4..504e43cb 100644 --- a/packages/react-front-kit-table/src/index.tsx +++ b/packages/react-front-kit-table/src/index.tsx @@ -1,5 +1,22 @@ /* eslint-disable react-refresh/only-export-components */ -export * from './Components/Table/Table'; -export * from './Components/TableGridView/TableGridView'; -export * from './helpers'; -export * from './types'; +'use client'; + +// component exports +export { Table } from './Components/Table/Table'; +export type { ITableProps } from './Components/Table/Table'; +export { TableGridView } from './Components/TableGridView/TableGridView'; +export type { + ITableGridAction, + ITableGridViewGridProps, + ITableGridViewProps, + ITableGridViewTableProps, +} from './Components/TableGridView/TableGridView'; +// helper exports +export { + getActionComponentProps, + getActionIcon, + getActionLabel, + isConfirmAction, +} from './helpers'; +// type exports +export type { ITableAction, ITableConfirmAction, ITableData } from './types'; diff --git a/packages/react-front-kit/CHANGELOG.md b/packages/react-front-kit/CHANGELOG.md index 2e7a31d6..2785f490 100644 --- a/packages/react-front-kit/CHANGELOG.md +++ b/packages/react-front-kit/CHANGELOG.md @@ -1,5 +1,30 @@ # @smile/react-front-kit +## 0.7.0 + +### Minor Changes + +- [#87](https://github.com/Smile-SA/react-front-kit/pull/87) [`dbc06ca`](https://github.com/Smile-SA/react-front-kit/commit/dbc06ca55961b69663ab7fdc02609c6525ae361d) Thanks [@tonai](https://github.com/tonai)! - Add theme provider and update barrel file for better next.js compatibility + +- [#93](https://github.com/Smile-SA/react-front-kit/pull/93) [`7415d9b`](https://github.com/Smile-SA/react-front-kit/commit/7415d9b9d119abfc850dda2ab6fa94845e72aee6) Thanks [@tonai](https://github.com/tonai)! - Add Preview component + +- [#95](https://github.com/Smile-SA/react-front-kit/pull/95) [`5696400`](https://github.com/Smile-SA/react-front-kit/commit/5696400e6f703da52db5f7199b50f8251fa76726) Thanks [@QuentinLeCaignec](https://github.com/QuentinLeCaignec)! - Added `FilterList` component, renamed `FiltersBar` in `SidebarFilters` + and `FiltersCheckboxList` in `SearchableCheckboxList`, + refactored `SearchableCheckboxList`, added common `IFilter` type in shared + package, added topContent prop to `SwitchableView`, fixed `BrowsingPage` with + name changes. + +- [#86](https://github.com/Smile-SA/react-front-kit/pull/86) [`eede698`](https://github.com/Smile-SA/react-front-kit/commit/eede698ce172e20eb1de4c70e3d59b7510afb9df) Thanks [@vapersmile](https://github.com/vapersmile)! - Add FitlersCheckboxList Component + +- [#88](https://github.com/Smile-SA/react-front-kit/pull/88) [`480af11`](https://github.com/Smile-SA/react-front-kit/commit/480af1122b41e28d938bd665c1aa998272c99d9a) Thanks [@QuentinLeCaignec](https://github.com/QuentinLeCaignec)! - Added `HeaderMobile` and `HeaderNav` components, reworked `Header` with mobile mode, added optional placeholder to `SearchBar`, improved info returned by collapseButtonProps in `SidebarMenu` + +- [#94](https://github.com/Smile-SA/react-front-kit/pull/94) [`70b674f`](https://github.com/Smile-SA/react-front-kit/commit/70b674f3513b4bf996e8f83a46c8a132ca3712ac) Thanks [@vapersmile](https://github.com/vapersmile)! - Add collapse function for infoCard component + +### Patch Changes + +- Updated dependencies [[`dbc06ca`](https://github.com/Smile-SA/react-front-kit/commit/dbc06ca55961b69663ab7fdc02609c6525ae361d), [`5696400`](https://github.com/Smile-SA/react-front-kit/commit/5696400e6f703da52db5f7199b50f8251fa76726)]: + - @smile/react-front-kit-shared@0.7.0 + ## 0.6.0 ### Minor Changes diff --git a/packages/react-front-kit/package.json b/packages/react-front-kit/package.json index 7360cb0f..da391d6e 100644 --- a/packages/react-front-kit/package.json +++ b/packages/react-front-kit/package.json @@ -1,6 +1,6 @@ { "name": "@smile/react-front-kit", - "version": "0.6.0", + "version": "0.7.0", "description": "React component library based on mantine", "license": "MIT", "homepage": "https://github.com/Smile-SA/react-front-kit", @@ -58,11 +58,10 @@ "scripts": { "build": "tsc --project tsconfig.build.json", "test": "jest", - "generate": "turbo gen react-component", "prepublishOnly": "npm run build && node ../../scripts/prepublish.mjs" }, "dependencies": { - "@smile/react-front-kit-shared": "0.6.0", + "@smile/react-front-kit-shared": "0.7.0", "pretty-bytes": "^6.1.1" }, "devDependencies": { diff --git a/packages/react-front-kit/src/Components/ActionBar/ActionBar.tsx b/packages/react-front-kit/src/Components/ActionBar/ActionBar.tsx index 389fdfe4..17847f69 100644 --- a/packages/react-front-kit/src/Components/ActionBar/ActionBar.tsx +++ b/packages/react-front-kit/src/Components/ActionBar/ActionBar.tsx @@ -19,7 +19,7 @@ const useStyles = createStyles((theme) => ({ alignItems: 'center', background: theme.fn.primaryColor(), borderRadius: 4, - color: 'white', + color: theme.white, display: 'inline-flex', justifyContent: 'space-between', padding: '16px 24px', @@ -28,7 +28,7 @@ const useStyles = createStyles((theme) => ({ })); export type IActionBarAction> = IAction< - Data | Data[] + Data[] >; export interface IActionBarProps> @@ -57,7 +57,7 @@ export function ActionBar>( const { classes } = useStyles(); - function setModal(action: IAction): void { + function setModal(action: IActionBarAction): void { setConfirmAction({ cancelColor: action.confirmModalProps?.cancelColor, cancelLabel: action.confirmModalProps?.cancelLabel, @@ -82,7 +82,7 @@ export function ActionBar>( handleClose(); } - function handleGridAction(action: IAction): void { + function handleGridAction(action: IActionBarAction): void { if (action.confirmation) { setModal(action); } else { diff --git a/packages/react-front-kit/src/Components/ActionBar/__snapshots__/ActionBar.test.tsx.snap b/packages/react-front-kit/src/Components/ActionBar/__snapshots__/ActionBar.test.tsx.snap index 05e1c4a0..35c9ab33 100644 --- a/packages/react-front-kit/src/Components/ActionBar/__snapshots__/ActionBar.test.tsx.snap +++ b/packages/react-front-kit/src/Components/ActionBar/__snapshots__/ActionBar.test.tsx.snap @@ -3,7 +3,7 @@ exports[`ActionBar matches snapshot 1`] = `
3 selected diff --git a/packages/react-front-kit/src/Components/CollapseButton/CollapseButtonControlled.tsx b/packages/react-front-kit/src/Components/CollapseButton/CollapseButtonControlled.tsx index 212efed7..f95bb14b 100644 --- a/packages/react-front-kit/src/Components/CollapseButton/CollapseButtonControlled.tsx +++ b/packages/react-front-kit/src/Components/CollapseButton/CollapseButtonControlled.tsx @@ -185,7 +185,7 @@ export function CollapseButtonControlled< {...buttonProps} > + ) : null} + + + + buttonLabel={manageFilterModalSearchSubmit} + checkboxes={filters} + onClickButton={handleManageFiltersSubmit} + placeholder={manageFilterModalSearchPlaceholder} + /> + + + ); +} diff --git a/packages/react-front-kit/src/Components/Header/Header.mock.tsx b/packages/react-front-kit/src/Components/Header/Header.mock.tsx new file mode 100644 index 00000000..cd7d61b3 --- /dev/null +++ b/packages/react-front-kit/src/Components/Header/Header.mock.tsx @@ -0,0 +1,63 @@ +import type { IHeaderNavMenu } from '../HeaderNav/HeaderNav'; +import type { ReactElement } from 'react'; + +import { Avatar, Menu } from '@mantine/core'; + +import { DropdownButton } from '../DropdownButton/DropdownButton'; +import { HeaderNav } from '../HeaderNav/HeaderNav'; + +export const menusMock: IHeaderNavMenu[] = [ + { + children: [ + { id: 4, label: 'Releases', url: '#' }, + { id: 5, label: 'Account', url: '#' }, + { + children: [ + { id: 8, label: 'Wiki pages', url: '#' }, + { id: 9, label: 'Settings', url: '#' }, + ], + id: 6, + label: 'Upcoming releases', + url: '#', + }, + { id: 7, label: 'Lorem ipsum dolor', url: '#' }, + ], + id: 1, + label: 'Espace documentaire', + url: '#', + }, + { id: 2, label: 'Espace workflow', url: '#' }, + { id: 3, label: 'Archives', url: '#' }, +]; + +export const childrenMock = (isMobile: boolean): ReactElement => { + return ; +}; + +export const leftContentMock = ( + logo +); + +export const rightContentMobileMock = ( + + + Calico + + + Espace RH + + + Aventure IA + + + Lunette & CO + + +); + +export const rightContentMock = ( + <> + {rightContentMobileMock} + + +); diff --git a/packages/react-front-kit/src/Components/Header/Header.stories.tsx b/packages/react-front-kit/src/Components/Header/Header.stories.tsx index 73bcfa3d..04252746 100644 --- a/packages/react-front-kit/src/Components/Header/Header.stories.tsx +++ b/packages/react-front-kit/src/Components/Header/Header.stories.tsx @@ -1,14 +1,18 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { Avatar, Menu } from '@mantine/core'; import { primaryTheme } from '@smile/react-front-kit-shared'; import { useStorybookArgsConnect } from '@smile/react-front-kit-shared/storybook-utils'; +import { action } from '@storybook/addon-actions'; import { expect } from '@storybook/jest'; import { userEvent, within } from '@storybook/testing-library'; -import { DropdownButton } from '../DropdownButton/DropdownButton'; - import { Header as Cmp } from './Header'; +import { + childrenMock, + leftContentMock, + rightContentMobileMock, + rightContentMock, +} from './Header.mock'; const meta = { argTypes: { @@ -46,35 +50,17 @@ type IStory = StoryObj; export const Header: IStory = { args: { - children: ( - <> - Espace documentaire - Espace workflow - Archives - - ), + children: childrenMock(false), childrenComponent: 'nav', + hasResponsiveMode: true, height: 90, - left: logo, - right: ( - <> - - - Calico - - - Espace RH - - - Aventure IA - - - Lunette & CO - - - - - ), + left: leftContentMock, + mobileProps: { + children: childrenMock(true), + right: rightContentMobileMock, + }, + onSearchSubmit: action('search input submitted'), + right: rightContentMock, searchTheme: primaryTheme, searchValue: '', withBorder: false, diff --git a/packages/react-front-kit/src/Components/Header/Header.style.tsx b/packages/react-front-kit/src/Components/Header/Header.style.tsx new file mode 100644 index 00000000..d6f8db01 --- /dev/null +++ b/packages/react-front-kit/src/Components/Header/Header.style.tsx @@ -0,0 +1,65 @@ +import { createStyles } from '@mantine/core'; + +export const useStyles = createStyles((theme) => ({ + around: { + alignItems: 'center', + gap: theme.spacing.xs, + }, + button: { + '&::after': { + background: + theme.colorScheme === 'light' + ? theme.colors.gray[3] + : theme.colors.gray[8], + content: '""', + display: 'block', + height: 36, + position: 'absolute', + right: 0, + top: '50%', + translate: '0 -50%', + width: 1, + }, + background: 'transparent', + borderRadius: 0, + position: 'relative', + }, + buttonOpened: { + '&::after': { + display: 'none', + }, + background: theme.fn.primaryColor(), + color: theme.white, + }, + container: { + alignItems: 'center', + justifyContent: 'space-between', + padding: '16px 64px', + position: 'relative', + width: '100%', + }, + menu: { + alignItems: 'center', + gap: theme.spacing.xl, + left: '50%', + margin: 'auto', + position: 'absolute', + top: '50%', + translate: '-50% -50%', + }, + none: { + display: 'none', + }, + sizeDesktop: { + display: 'initial', + [theme.fn.smallerThan('md')]: { + display: 'none', + }, + }, + sizeMobile: { + display: 'none', + [theme.fn.smallerThan('md')]: { + display: 'initial', + }, + }, +})); diff --git a/packages/react-front-kit/src/Components/Header/Header.test.tsx b/packages/react-front-kit/src/Components/Header/Header.test.tsx index ac28cf48..1c50c463 100644 --- a/packages/react-front-kit/src/Components/Header/Header.test.tsx +++ b/packages/react-front-kit/src/Components/Header/Header.test.tsx @@ -5,7 +5,7 @@ import { Header } from './Header'; describe('Header', () => { it('matches snapshot', () => { const { container } = renderWithProviders( -
+
Espace documentaire Espace workflow Archives diff --git a/packages/react-front-kit/src/Components/Header/Header.tsx b/packages/react-front-kit/src/Components/Header/Header.tsx index 37a65d08..a83a8899 100644 --- a/packages/react-front-kit/src/Components/Header/Header.tsx +++ b/packages/react-front-kit/src/Components/Header/Header.tsx @@ -1,6 +1,11 @@ 'use client'; -import type { HeaderProps, MantineThemeOverride } from '@mantine/core'; +import type { IHeaderMobileProps } from '../HeaderMobile/HeaderMobile'; +import type { + HeaderProps, + MantineThemeOverride, + TextInputProps, +} from '@mantine/core'; import type { ElementType, FormEvent, ReactElement, ReactNode } from 'react'; import { @@ -8,74 +13,30 @@ import { Flex, Header as MantineHeader, MantineProvider, - createStyles, useMantineTheme, } from '@mantine/core'; import { useClickOutside } from '@mantine/hooks'; import { MagnifyingGlass } from '@phosphor-icons/react'; import { useState } from 'react'; +import { HeaderMobile } from '../HeaderMobile/HeaderMobile'; import { HeaderSearch } from '../HeaderSearch/HeaderSearch'; -const useStyles = createStyles((theme) => ({ - around: { - alignItems: 'center', - gap: theme.spacing.xs, - }, - button: { - '&::after': { - background: - theme.colorScheme === 'light' - ? theme.colors.gray[3] - : theme.colors.gray[8], - content: '""', - display: 'block', - height: 36, - position: 'absolute', - right: 0, - top: '50%', - translate: '0 -50%', - width: 1, - }, - background: 'transparent', - borderRadius: 0, - position: 'relative', - }, - buttonOpened: { - '&::after': { - display: 'none', - }, - background: theme.fn.primaryColor(), - color: theme.white, - }, - container: { - alignItems: 'center', - justifyContent: 'space-between', - padding: '16px 64px', - position: 'relative', - width: '100%', - }, - menu: { - alignItems: 'center', - gap: theme.spacing.xl, - left: '50%', - margin: 'auto', - position: 'absolute', - top: '50%', - translate: '-50% -50%', - }, -})); +import { useStyles } from './Header.style'; export interface IHeaderProps extends Omit { + children: ReactNode; childrenComponent?: ElementType; + hasResponsiveMode?: boolean; hasSearch?: boolean; height?: number; left?: ReactNode; + mobileProps?: IHeaderMobileProps; onSearchChange?: (value: string) => void; onSearchSubmit?: (event: FormEvent) => void; right?: ReactNode; - searchAriaLabel?: string; + searchInputProps?: Omit; searchTheme?: MantineThemeOverride; searchValue?: string; } @@ -85,76 +46,108 @@ export function Header(props: IHeaderProps): ReactElement { const { children, childrenComponent, + hasResponsiveMode = true, hasSearch = true, height = 90, left, onSearchChange, onSearchSubmit, right, - searchAriaLabel = 'Search', + mobileProps, + searchInputProps, searchTheme, searchValue, withBorder = true, ...headerProps } = props; - const { classes } = useStyles(); - const [opened, setOpened] = useState(false); - const header = useClickOutside(() => setOpened(false)); + const defaultMobileProps = { + children, + left, + right, + searchInputProps, + ...mobileProps, + }; + + const [searchOpened, setSearchOpened] = useState(false); + const searchButtonRef = useClickOutside(() => setSearchOpened(false)); + const defaultTheme = useMantineTheme(); + const { classes } = useStyles(); function handleClick(): void { - setOpened(!opened); + setSearchOpened(!searchOpened); } const buttonClasses = [classes.button]; - if (opened) { + if (searchOpened) { buttonClasses.push(classes.buttonOpened); } return ( - - - {left} - + {/* Desktop Header */} +
+ - {children} - - - {Boolean(hasSearch) && ( - - )} - {Boolean(opened) && ( - - - - )} - {right} - - - + {children} + + +
+ {Boolean(hasSearch) && ( + + )} + {Boolean(searchOpened) && ( + + + + )} +
+ {right} +
+ + +
+ {/* Mobile Header */} +
+ +
+ ); } diff --git a/packages/react-front-kit/src/Components/Header/__snapshots__/Header.test.tsx.snap b/packages/react-front-kit/src/Components/Header/__snapshots__/Header.test.tsx.snap index 7b12d0a2..0e979f0d 100644 --- a/packages/react-front-kit/src/Components/Header/__snapshots__/Header.test.tsx.snap +++ b/packages/react-front-kit/src/Components/Header/__snapshots__/Header.test.tsx.snap @@ -2,66 +2,170 @@ exports[`Header matches snapshot 1`] = `
`; diff --git a/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.stories.tsx b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.stories.tsx new file mode 100644 index 00000000..c7807cc4 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.stories.tsx @@ -0,0 +1,44 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { useStorybookArgsConnect } from '@smile/react-front-kit-shared/storybook-utils'; +import { action } from '@storybook/addon-actions'; + +import { + childrenMock, + leftContentMock, + rightContentMobileMock, +} from '../Header/Header.mock'; + +import { HeaderMobile as Cmp } from './HeaderMobile'; + +const meta = { + component: Cmp, + decorators: [ + function Component(Story, ctx) { + const args = useStorybookArgsConnect(ctx.args, { + onSearchChange: 'searchValue', + }); + return ; + }, + ], + parameters: { + layout: 'fullscreen', + }, + tags: ['autodocs'], + title: '3-custom/Components/HeaderMobile', +} satisfies Meta; + +export default meta; +type IStory = StoryObj; + +export const HeaderMobile: IStory = { + args: { + children: childrenMock(true), + height: 90, + left: leftContentMock, + onSearchSubmit: action('search input submitted'), + right: rightContentMobileMock, + searchValue: '', + withBorder: false, + }, +}; diff --git a/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.style.tsx b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.style.tsx new file mode 100644 index 00000000..7b72af73 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.style.tsx @@ -0,0 +1,43 @@ +import { createStyles } from '@mantine/core'; + +export const useStyles = createStyles((theme, height: number) => ({ + around: { + alignItems: 'center', + gap: theme.spacing.xs, + }, + burgerMenu: { + backgroundColor: theme.white, + borderTop: '1px solid', + borderTopColor: theme.colors.gray[2], + height: `calc(100vh - ${height}px)`, + padding: '40px 24px', + }, + burgerSearch: { + '.mantine-TextInput-input': { + padding: '6px 68px 6px 24px', + }, + marginTop: 'auto', + }, + containerMobile: { + alignItems: 'center', + justifyContent: 'space-between', + padding: '16px 24px', + position: 'relative', + width: '100%', + }, + menu: { + alignItems: 'center', + gap: theme.spacing.xl, + left: '50%', + margin: 'auto', + position: 'absolute', + top: '50%', + translate: '-50% -50%', + }, + searchIcon: { + marginRight: '24px', + }, + separator: { + borderColor: theme.colors.gray[3], + }, +})); diff --git a/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.test.tsx b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.test.tsx new file mode 100644 index 00000000..d00a9ddf --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.test.tsx @@ -0,0 +1,16 @@ +import { renderWithProviders } from '@smile/react-front-kit-shared/test-utils'; + +import { HeaderMobile } from './HeaderMobile'; + +describe('HeaderMobile', () => { + it('matches snapshot', () => { + const { container } = renderWithProviders( + + Espace documentaire + Espace workflow + Archives + , + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.tsx b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.tsx new file mode 100644 index 00000000..6a237285 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/HeaderMobile.tsx @@ -0,0 +1,107 @@ +'use client'; + +import type { + BurgerProps, + CollapseProps, + HeaderProps, + TextInputProps, +} from '@mantine/core'; +import type { FormEvent, ReactElement, ReactNode } from 'react'; + +import { + Burger, + Collapse, + Divider, + Flex, + Header as MantineHeader, + Stack, + TextInput, + useMantineTheme, +} from '@mantine/core'; +import { MagnifyingGlass } from '@phosphor-icons/react'; +import { useState } from 'react'; + +import { useStyles } from './HeaderMobile.style'; + +export interface IHeaderMobileProps + extends Omit { + burgerProps?: Omit; + children?: ReactNode; + collapseProps?: Omit; + hasSearch?: boolean; + height?: number; + left?: ReactNode; + onSearchChange?: (value: string) => void; + onSearchSubmit?: (event: FormEvent) => void; + right?: ReactNode; + searchInputProps?: Omit; + searchValue?: string; +} + +/** Additional props will be forwarded to the [Mantine AppShell (Header) component](https://mantine.dev/core/app-shell/) */ +export function HeaderMobile(props: IHeaderMobileProps): ReactElement { + const { + burgerProps, + children, + collapseProps, + hasSearch = true, + height = 76, + left, + onSearchChange, + onSearchSubmit, + right, + searchInputProps, + searchValue, + withBorder = true, + ...headerProps + } = props; + const [burgerOpened, setBurgerOpened] = useState(false); + + const theme = useMantineTheme(); + const { classes } = useStyles(height); + + return ( + + + + setBurgerOpened((o) => !o)} + opened={burgerOpened} + size={22} + title="Open navigation" + {...burgerProps} + /> + + + {left} + {right} + + + + {children} + {Boolean(hasSearch) && ( +
+ onSearchChange?.(e.target.value)} + placeholder="Search on the site" + radius={32} + rightSection={ + + } + size="md" + value={searchValue} + {...searchInputProps} + /> + + )} +
+
+
+ ); +} diff --git a/packages/react-front-kit/src/Components/HeaderMobile/__snapshots__/HeaderMobile.test.tsx.snap b/packages/react-front-kit/src/Components/HeaderMobile/__snapshots__/HeaderMobile.test.tsx.snap new file mode 100644 index 00000000..282c86e0 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderMobile/__snapshots__/HeaderMobile.test.tsx.snap @@ -0,0 +1,102 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HeaderMobile matches snapshot 1`] = ` +
+
+
+
+ + +
+
+
+ +
+
+`; diff --git a/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.stories.tsx b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.stories.tsx new file mode 100644 index 00000000..c4a77cee --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.stories.tsx @@ -0,0 +1,34 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Flex } from '@mantine/core'; + +import { menusMock } from '../Header/Header.mock'; + +import { HeaderNav as Cmp } from './HeaderNav'; + +const meta = { + component: Cmp, + tags: ['autodocs'], + title: '3-custom/Components/HeaderNav', +} satisfies Meta; + +export default meta; +type IStory = StoryObj; + +export const HeaderNav: IStory = { + args: { + menus: menusMock, + }, + render: ({ ...props }) => ( + + + + ), +}; + +export const Mobile: IStory = { + args: { + isMobile: true, + menus: menusMock, + }, +}; diff --git a/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.style.tsx b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.style.tsx new file mode 100644 index 00000000..2660f338 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.style.tsx @@ -0,0 +1,47 @@ +import { createStyles } from '@mantine/core'; + +export const useStyles = createStyles((theme) => ({ + activeLabel: { + '& span': { + color: theme.colors.dark[6], + }, + }, + activeRootLabel: { + '& span': { + color: theme.fn.primaryColor(), + }, + }, + dropdown: { + borderRadius: 10, + padding: '16px 20px', + }, + menu: { + ':hover': { backgroundColor: theme.colors.gray[1] }, + backgroundColor: 'transparent', + }, + navLink: { + borderRadius: 10, + width: 'unset', + }, + navLinkLabel: { + fontSize: '14px', + fontWeight: 400, + margin: 0, + padding: 0, + }, + navLinkParentLabel: { + fontSize: '14px', + fontWeight: 600, + margin: 0, + padding: 0, + }, + rootMenu: { + '& span': { + color: theme.colors.dark[6], + }, + ':not(:first-of-type)': { + marginTop: '32px', + }, + fontSize: '18px', + }, +})); diff --git a/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.test.tsx b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.test.tsx new file mode 100644 index 00000000..77d2dd92 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.test.tsx @@ -0,0 +1,22 @@ +import { renderWithProviders } from '@smile/react-front-kit-shared/test-utils'; + +import { menusMock } from '../Header/Header.mock'; + +import { HeaderNav } from './HeaderNav'; + +describe('HeaderNav', () => { + beforeEach(() => { + // Prevent mantine random ID + Math.random = () => 0.42; + }); + it('matches snapshot in desktop mode', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + it('matches snapshot in mobile mode', () => { + const { container } = renderWithProviders( + , + ); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.tsx b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.tsx new file mode 100644 index 00000000..620afbe8 --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/HeaderNav.tsx @@ -0,0 +1,122 @@ +'use client'; + +import type { IMenuItem } from '../SidebarMenu/SidebarMenu'; +import type { ElementType, ReactElement, ReactNode } from 'react'; + +import { Menu, NavLink, useMantineTheme } from '@mantine/core'; +import { CaretDown } from '@phosphor-icons/react'; + +import { SidebarMenu } from '../SidebarMenu/SidebarMenu'; + +import { useStyles } from './HeaderNav.style'; + +function recursiveNavLinks( + menus: IHeaderNavMenu[], + component: ElementType, + className: string, +): ReactNode { + return menus.map((menu) => ( + + {menu.children + ? recursiveNavLinks(menu.children, component, className) + : null} + + )); +} + +export interface IHeaderNavMenu + extends IMenuItem { + children?: IHeaderNavMenu[]; + url?: string; +} + +export interface IHeaderNavProps { + isMobile?: boolean; + menus: IHeaderNavMenu[]; + navLinkComponent?: ElementType; +} + +export function HeaderNav( + props: IHeaderNavProps, +): ReactElement { + const { menus, isMobile = false, navLinkComponent = 'a' } = props; + const theme = useMantineTheme(); + const { classes } = useStyles(); + + return ( + <> + {isMobile ? ( + , + level: number, + isSelected: boolean, + opened: boolean, + ) => { + return { + className: [ + classes.menu, + level === 0 ? classes.rootMenu : '', + opened || isSelected + ? level === 0 + ? classes.activeRootLabel + : classes.activeLabel + : '', + ].join(' '), + ...(level >= 1 && { + collapseProps: { + className: '', + }, + }), + }; + }} + menu={menus} + p={0} + /> + ) : ( + menus.map((menu) => { + if (menu.children) { + return ( + + + + } + /> + + + {recursiveNavLinks( + menu.children, + navLinkComponent, + classes.navLink, + )} + + + ); + } + return ( + + ); + }) + )} + + ); +} diff --git a/packages/react-front-kit/src/Components/HeaderNav/__snapshots__/HeaderNav.test.tsx.snap b/packages/react-front-kit/src/Components/HeaderNav/__snapshots__/HeaderNav.test.tsx.snap new file mode 100644 index 00000000..8901705a --- /dev/null +++ b/packages/react-front-kit/src/Components/HeaderNav/__snapshots__/HeaderNav.test.tsx.snap @@ -0,0 +1,404 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HeaderNav matches snapshot in desktop mode 1`] = ` +
+ +