From d497ba9dd9cd9a696fb5c6d749e725b4e0c44af4 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Wed, 18 Sep 2024 10:51:57 +0200 Subject: [PATCH 1/8] Add missing Mui theme variables & use "const assertion" for styles and check rules --- src/components/authentication/Login.tsx | 13 ++--- src/components/authentication/Logout.tsx | 11 ++-- .../cardErrorBoundary/CardErrorBoundary.tsx | 5 +- .../DraggableClickableCheckBoxItem.tsx | 5 +- .../DraggableClickableRowItem.tsx | 5 +- .../checkBoxList/checkBoxList.type.ts | 22 ++++---- .../customAGGrid/customAggrid.style.ts | 10 ++-- src/components/customAGGrid/customAggrid.tsx | 16 +++--- .../customMuiDialog/CustomMuiDialog.tsx | 3 +- .../DirectoryItemSelector.tsx | 10 ++-- .../elementItem/EquipmentItem.tsx | 16 +++--- .../elementSearch/tagRenderer/TagRenderer.tsx | 9 ++-- .../expandableGroup/ExpandableGroup.tsx | 15 +++--- .../flatParameters/FlatParameters.tsx | 6 +-- .../reactHookForm/DirectoryItemsInput.tsx | 8 +-- .../agGridTable/CustomAgGridTable.tsx | 32 ++++++------ .../reactHookForm/numbers/RangeInput.tsx | 16 +++--- .../reactHookForm/text/ExpandingTextField.tsx | 19 +++---- .../utils/TextFieldWithAdornment.tsx | 5 +- .../AutocompleteWithFavorites.tsx | 9 ++-- .../inputs/reactQueryBuilder/ValueEditor.tsx | 3 +- .../compositeRuleEditor/GroupValueEditor.tsx | 7 +-- .../compositeRuleEditor/RuleValueEditor.tsx | 3 +- .../muiVirtualizedTable/ColumnHeader.tsx | 11 ++-- .../MuiVirtualizedTable.tsx | 13 +++-- .../overflowableText/OverflowableText.tsx | 13 ++--- src/components/topBar/AboutDialog.tsx | 30 +++++------ src/components/topBar/GridLogo.tsx | 8 +-- src/components/topBar/TopBar.tsx | 9 ++-- .../treeViewFinder/TreeViewFinder.tsx | 17 +++---- src/index.ts | 2 + src/module-emotion.d.ts | 18 +++++++ src/module-mui.d.ts | 50 ++++++++++++++----- src/utils/mapper/elementIcon.tsx | 4 +- src/utils/styles.ts | 13 ++++- src/utils/types/equipmentType.ts | 3 +- 36 files changed, 246 insertions(+), 193 deletions(-) create mode 100644 src/module-emotion.d.ts diff --git a/src/components/authentication/Login.tsx b/src/components/authentication/Login.tsx index 4d4121f51..73ac9df12 100644 --- a/src/components/authentication/Login.tsx +++ b/src/components/authentication/Login.tsx @@ -4,22 +4,23 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Avatar, Box, Button, Container, Link, Theme, Typography } from '@mui/material'; +import { Avatar, Box, Button, Container, Link, Typography } from '@mui/material'; import { LockOutlined as LockOutlinedIcon } from '@mui/icons-material'; import { FormattedMessage } from 'react-intl'; +import { MuiStyles } from '../../utils/styles'; const styles = { - paper: (theme: Theme) => ({ + paper: (theme) => ({ marginTop: theme.spacing(8), display: 'flex', flexDirection: 'column', alignItems: 'center', }), - avatar: (theme: Theme) => ({ + avatar: (theme) => ({ margin: theme.spacing(1), backgroundColor: theme.palette.secondary.main, }), - submit: (theme: Theme) => ({ + submit: (theme) => ({ margin: theme.spacing(3, 0, 2), borderRadius: '30px', }), @@ -27,14 +28,14 @@ const styles = { width: 64, height: 64, }, -}; +} as const satisfies MuiStyles; export interface LoginProps { onLoginClick: () => void; disabled: boolean; } -function Login({ onLoginClick, disabled }: LoginProps) { +function Login({ onLoginClick, disabled }: Readonly) { return ( diff --git a/src/components/authentication/Logout.tsx b/src/components/authentication/Logout.tsx index 8ea21e296..064d1f50a 100644 --- a/src/components/authentication/Logout.tsx +++ b/src/components/authentication/Logout.tsx @@ -5,26 +5,27 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Avatar, Box, Button, Container, Link, Theme, Typography } from '@mui/material'; +import { Avatar, Box, Button, Container, Link, Typography } from '@mui/material'; import { LogoutOutlined as LogoutOutlinedIcon } from '@mui/icons-material'; import { FormattedMessage } from 'react-intl'; +import { MuiStyles } from '../../utils/styles'; const styles = { - paper: (theme: Theme) => ({ + paper: (theme) => ({ marginTop: theme.spacing(8), display: 'flex', flexDirection: 'column', alignItems: 'center', }), - avatar: (theme: Theme) => ({ + avatar: (theme) => ({ margin: theme.spacing(1), backgroundColor: theme.palette.error.main, }), - submit: (theme: Theme) => ({ + submit: (theme) => ({ margin: theme.spacing(3, 0, 2), borderRadius: '30px', }), -}; +} as const satisfies MuiStyles; export interface LogoutProps { onLogoutClick: () => void; diff --git a/src/components/cardErrorBoundary/CardErrorBoundary.tsx b/src/components/cardErrorBoundary/CardErrorBoundary.tsx index c3f86a50b..b2ad4605e 100644 --- a/src/components/cardErrorBoundary/CardErrorBoundary.tsx +++ b/src/components/cardErrorBoundary/CardErrorBoundary.tsx @@ -19,9 +19,8 @@ import { Collapse, IconButton, IconButtonProps, - Theme, - Typography, styled, + Typography, } from '@mui/material'; import { Component, ErrorInfo, ReactNode } from 'react'; import { FormattedMessage } from 'react-intl'; @@ -33,7 +32,7 @@ export interface ExpandMoreProps extends IconButtonProps { const ExpandMore = styled((props: ExpandMoreProps) => { const { expand, ...other } = props; return ; -})(({ theme, expand }: { theme: Theme; expand: boolean }) => ({ +})(({ theme, expand }) => ({ transform: !expand ? 'rotate(0deg)' : 'rotate(180deg)', marginLeft: 'auto', transition: theme.transitions.create('transform', { diff --git a/src/components/checkBoxList/DraggableClickableCheckBoxItem.tsx b/src/components/checkBoxList/DraggableClickableCheckBoxItem.tsx index ae035fd16..76addbe81 100644 --- a/src/components/checkBoxList/DraggableClickableCheckBoxItem.tsx +++ b/src/components/checkBoxList/DraggableClickableCheckBoxItem.tsx @@ -9,15 +9,16 @@ import { Checkbox, IconButton, ListItemIcon, ListItemText } from '@mui/material' import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; import OverflowableText from '../overflowableText'; import { DraggableClickableItemProps } from './checkBoxList.type'; +import { MuiStyles } from '../../utils/styles'; const styles = { - dragIcon: (theme: any) => ({ + dragIcon: (theme) => ({ padding: 'unset', border: theme.spacing(0), borderRadius: theme.spacing(0), zIndex: 90, }), -}; +} as const satisfies MuiStyles; export function DraggableClickableCheckBoxItem({ sx, diff --git a/src/components/checkBoxList/DraggableClickableRowItem.tsx b/src/components/checkBoxList/DraggableClickableRowItem.tsx index de5590904..1c74cfad3 100644 --- a/src/components/checkBoxList/DraggableClickableRowItem.tsx +++ b/src/components/checkBoxList/DraggableClickableRowItem.tsx @@ -9,15 +9,16 @@ import { Checkbox, IconButton, ListItemButton, ListItemIcon, ListItemText } from import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; import OverflowableText from '../overflowableText'; import { DraggableClickableItemProps } from './checkBoxList.type'; +import { MuiStyles } from '../../utils/styles'; const styles = { - dragIcon: (theme: any) => ({ + dragIcon: (theme) => ({ padding: 'unset', border: theme.spacing(0), borderRadius: theme.spacing(0), zIndex: 90, }), -}; +} as const satisfies MuiStyles; export function DraggableClickableRowItem({ sx, diff --git a/src/components/checkBoxList/checkBoxList.type.ts b/src/components/checkBoxList/checkBoxList.type.ts index 24b3e750b..39e8243cf 100644 --- a/src/components/checkBoxList/checkBoxList.type.ts +++ b/src/components/checkBoxList/checkBoxList.type.ts @@ -5,18 +5,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import React from 'react'; +import { ReactElement } from 'react'; import { DraggableProvided, DragStart, DropResult } from 'react-beautiful-dnd'; -import { SxProps } from '@mui/system'; +import { MuiStyle } from '../../utils/styles'; export interface CheckBoxListItemSxProps { - checkBoxIcon?: SxProps; - label?: SxProps; - checkboxListItem?: SxProps; - checkboxButton?: SxProps; - checkbox?: SxProps; - dragAndDropContainer?: SxProps; - checkboxList?: SxProps; + checkBoxIcon?: MuiStyle; + label?: MuiStyle; + checkboxListItem?: MuiStyle; + checkboxButton?: MuiStyle; + checkbox?: MuiStyle; + dragAndDropContainer?: MuiStyle; + checkboxList?: MuiStyle; } export interface CheckBoxListItemProps { @@ -24,7 +24,7 @@ export interface CheckBoxListItemProps { sx?: CheckBoxListItemSxProps; label: string; onClick: () => void; - secondaryAction?: (item: T, hover: string) => React.ReactElement | null; + secondaryAction?: (item: T, hover: string) => ReactElement | null; getItemId: (item: T) => string; disabled?: boolean; divider?: boolean; @@ -43,7 +43,7 @@ export interface CheckBoxListItemsProps { onSelectionChange?: (selectedItems: T[]) => void; getItemId: (item: T) => string; getItemLabel?: (item: T) => string; - secondaryAction?: (item: T) => React.ReactElement | null; + secondaryAction?: (item: T) => ReactElement | null; enableSecondaryActionOnHover?: boolean; isDisabled?: (item: T) => boolean; addSelectAllCheckbox?: boolean; diff --git a/src/components/customAGGrid/customAggrid.style.ts b/src/components/customAGGrid/customAggrid.style.ts index 9ad9c15d9..aa8bd8c95 100644 --- a/src/components/customAGGrid/customAggrid.style.ts +++ b/src/components/customAGGrid/customAggrid.style.ts @@ -4,13 +4,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Theme } from '@mui/material/styles/createTheme'; -import { SystemStyleObject } from '@mui/system/styleFunctionSx/styleFunctionSx'; + +import { MuiStyles } from '../../utils/styles'; export const CUSTOM_AGGRID_THEME = 'custom-aggrid-theme'; export const styles = { - grid: (theme: Theme): SystemStyleObject => ({ + grid: (theme) => ({ width: 'auto', height: '100%', position: 'relative', @@ -49,7 +49,7 @@ export const styles = { borderRight: 'none', }, }, - overlayBackground: (theme: Theme) => ({ + overlayBackground: (theme) => ({ '& .ag-overlay-loading-wrapper': { background: theme.aggrid.overlay.background, }, @@ -57,4 +57,4 @@ export const styles = { background: 'none', }, }), -}; +} as const satisfies MuiStyles; diff --git a/src/components/customAGGrid/customAggrid.tsx b/src/components/customAGGrid/customAggrid.tsx index 2dacd7c43..a8ab1424d 100644 --- a/src/components/customAGGrid/customAggrid.tsx +++ b/src/components/customAGGrid/customAggrid.tsx @@ -5,17 +5,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import React, { useCallback } from 'react'; -import { Theme } from '@mui/material/styles/createTheme'; +import { forwardRef, useCallback } from 'react'; +import { Box, useTheme } from '@mui/material'; import { AgGridReact, AgGridReactProps } from 'ag-grid-react'; import { useIntl } from 'react-intl'; import 'ag-grid-community/styles/ag-grid.css'; import 'ag-grid-community/styles/ag-theme-alpine.css'; import { ColumnResizedEvent, GetLocaleTextParams } from 'ag-grid-community'; -import { Box } from '@mui/system'; -import { SxProps, useTheme } from '@mui/material'; import { mergeSx } from '../../utils/styles'; -import { styles, CUSTOM_AGGRID_THEME } from './customAggrid.style'; +import { CUSTOM_AGGRID_THEME, styles } from './customAggrid.style'; interface CustomAGGGridStyleProps { shouldHidePinnedHeaderRightBorder?: boolean; @@ -36,7 +34,7 @@ const onColumnResized = (params: ColumnResizedEvent) => { } }; -const CustomAGGrid = React.forwardRef((props, ref) => { +const CustomAGGrid = forwardRef((props, ref) => { const { shouldHidePinnedHeaderRightBorder = false, overlayNoRowsTemplate, @@ -44,7 +42,7 @@ const CustomAGGrid = React.forwardRef((props, re loadingOverlayComponentParams, showOverlay = false, } = props; - const theme = useTheme(); + const theme = useTheme(); const intl = useIntl(); const GRID_PREFIX = 'grid.'; @@ -63,9 +61,9 @@ const CustomAGGrid = React.forwardRef((props, re return ( diff --git a/src/components/dialogs/customMuiDialog/CustomMuiDialog.tsx b/src/components/dialogs/customMuiDialog/CustomMuiDialog.tsx index 5972af876..8433898b0 100644 --- a/src/components/dialogs/customMuiDialog/CustomMuiDialog.tsx +++ b/src/components/dialogs/customMuiDialog/CustomMuiDialog.tsx @@ -14,6 +14,7 @@ import SubmitButton from '../../inputs/reactHookForm/utils/SubmitButton'; import CancelButton from '../../inputs/reactHookForm/utils/CancelButton'; import CustomFormProvider, { MergedFormContextProps } from '../../inputs/reactHookForm/provider/CustomFormProvider'; import PopupConfirmationDialog from '../popupConfirmationDialog/PopupConfirmationDialog'; +import { MuiStyles } from '../../../utils/styles'; export interface CustomMuiDialogProps { open: boolean; @@ -40,7 +41,7 @@ const styles = { margin: 'auto', }, }, -}; +} as const satisfies MuiStyles; function CustomMuiDialog({ open, diff --git a/src/components/directoryItemSelector/DirectoryItemSelector.tsx b/src/components/directoryItemSelector/DirectoryItemSelector.tsx index 86ca64e64..daad38835 100644 --- a/src/components/directoryItemSelector/DirectoryItemSelector.tsx +++ b/src/components/directoryItemSelector/DirectoryItemSelector.tsx @@ -6,21 +6,21 @@ */ import { useCallback, useEffect, useRef, useState } from 'react'; -import { SxProps, Theme } from '@mui/material'; import { UUID } from 'crypto'; import getFileIcon from '../../utils/mapper/elementIcon'; import { ElementType } from '../../utils/types/elementType'; import TreeViewFinder, { TreeViewFinderNodeProps, TreeViewFinderProps } from '../treeViewFinder/TreeViewFinder'; import { useSnackMessage } from '../../hooks/useSnackMessage'; import { fetchDirectoryContent, fetchElementsInfos, fetchRootFolders } from '../../services'; +import { MuiStyles } from '../../utils/styles'; const styles = { - icon: (theme: Theme) => ({ + icon: (theme) => ({ marginRight: theme.spacing(1), width: '18px', height: '18px', }), -}; +} as const satisfies MuiStyles; function sameRights(a: any, b: any) { if (!a && !b) { @@ -183,7 +183,7 @@ function DirectoryItemSelector({ id: e.elementUuid, name: e.elementName, specificMetadata: e.specificMetadata, - icon: getFileIcon(e.type, styles.icon as SxProps), + icon: getFileIcon(e.type, styles.icon), children: e.type === ElementType.DIRECTORY ? convertChildren(e.children) : undefined, childrenCount: e.type === ElementType.DIRECTORY ? e.subdirectoriesCount : undefined, }; @@ -196,7 +196,7 @@ function DirectoryItemSelector({ return { id: e.elementUuid, name: e.elementName, - icon: getFileIcon(e.type, styles.icon as SxProps), + icon: getFileIcon(e.type, styles.icon), children: e.type === ElementType.DIRECTORY ? convertChildren(nodeMap.current[e.elementUuid].children) diff --git a/src/components/elementSearch/elementItem/EquipmentItem.tsx b/src/components/elementSearch/elementItem/EquipmentItem.tsx index c333f10bd..793835f10 100644 --- a/src/components/elementSearch/elementItem/EquipmentItem.tsx +++ b/src/components/elementSearch/elementItem/EquipmentItem.tsx @@ -8,11 +8,11 @@ import match from 'autosuggest-highlight/match'; import parse from 'autosuggest-highlight/parse'; import clsx from 'clsx'; import { FormattedMessage } from 'react-intl'; -import { Box, SxProps, Theme } from '@mui/material'; +import { Box } from '@mui/material'; import { EQUIPMENT_TYPE, EquipmentInfos } from '../../../utils/types/equipmentType'; import { TagRenderer } from '../index'; import OverflowableText from '../../overflowableText'; -import { mergeSx } from '../../../utils/styles'; +import { mergeSx, MuiStyle } from '../../../utils/styles'; export interface EquipmentItemProps { inputValue: string; @@ -27,11 +27,11 @@ export interface EquipmentItemProps { equipmentVlTag?: string; }; styles?: { - result?: SxProps; - equipmentOption?: SxProps; - equipmentTag?: SxProps; - equipmentTypeTag?: SxProps; - equipmentVlTag?: SxProps; + result?: MuiStyle; + equipmentOption?: MuiStyle; + equipmentTag?: MuiStyle; + equipmentTypeTag?: MuiStyle; + equipmentVlTag?: MuiStyle; }; } @@ -41,7 +41,7 @@ export function EquipmentItem({ element, showsJustText = false, ...props -}: EquipmentItemProps) { +}: Readonly) { const matches = match(element.label, inputValue, { insideWords: true, findAllOccurrences: true, diff --git a/src/components/elementSearch/tagRenderer/TagRenderer.tsx b/src/components/elementSearch/tagRenderer/TagRenderer.tsx index 65fad6f3d..1c1ac3b60 100644 --- a/src/components/elementSearch/tagRenderer/TagRenderer.tsx +++ b/src/components/elementSearch/tagRenderer/TagRenderer.tsx @@ -5,10 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import clsx from 'clsx'; -import { SxProps, Theme } from '@mui/material'; import OverflowableText from '../../overflowableText'; import { EQUIPMENT_TYPE, EquipmentInfos } from '../../../utils/types/equipmentType'; -import { mergeSx } from '../../../utils/styles'; +import { mergeSx, MuiStyle } from '../../../utils/styles'; export interface TagRendererProps { element: EquipmentInfos; @@ -17,12 +16,12 @@ export interface TagRendererProps { equipmentVlTag?: string; }; styles?: { - equipmentTag?: SxProps; - equipmentVlTag?: SxProps; + equipmentTag?: MuiStyle; + equipmentVlTag?: MuiStyle; }; } -function TagRenderer({ element, ...props }: TagRendererProps) { +function TagRenderer({ element, ...props }: Readonly) { if (element.type !== EQUIPMENT_TYPE.SUBSTATION?.name && element.type !== EQUIPMENT_TYPE.VOLTAGE_LEVEL?.name) { return ( ({ + accordion: { '&:before': { display: 'none', }, background: 'none', - }), - accordionSummary: (theme: Theme) => ({ + }, + accordionSummary: (theme) => ({ flexDirection: 'row-reverse', // place icon at the left padding: 0, // reset default left right space in summary '.MuiAccordionSummary-content': { @@ -35,10 +36,10 @@ export const styles = { transform: 'rotate(0deg)', }, }), - accordionDetails: () => ({ + accordionDetails: { padding: 0, // reset default left right space in details - }), -}; + }, +} as const satisfies MuiStyles; export interface ExpandableGroupProps extends PropsWithChildren { renderHeader: ReactNode; diff --git a/src/components/flatParameters/FlatParameters.tsx b/src/components/flatParameters/FlatParameters.tsx index ae24cdbbf..d8d0d4ed8 100644 --- a/src/components/flatParameters/FlatParameters.tsx +++ b/src/components/flatParameters/FlatParameters.tsx @@ -18,20 +18,20 @@ import { Switch, TextField, TextFieldProps, - Theme, Tooltip, Typography, } from '@mui/material'; import { Tune as TuneIcon } from '@mui/icons-material'; import { FormattedMessage, useIntl } from 'react-intl'; import MultipleSelectionDialog from '../multipleSelectionDialog/MultipleSelectionDialog'; +import { MuiStyles } from '../../utils/styles'; const styles = { paramList: { width: '100%', margin: 0, }, - paramListItem: (theme: Theme) => ({ + paramListItem: (theme) => ({ display: 'flex', justifyContent: 'space-between', gap: theme.spacing(2), @@ -43,7 +43,7 @@ const styles = { minWidth: '30%', overflowWrap: 'anywhere', }, -}; +} as const satisfies MuiStyles; const FloatRE = /^-?\d*[.,]?\d*([eE]-?\d*)?$/; const IntegerRE = /^-?\d*$/; diff --git a/src/components/inputs/reactHookForm/DirectoryItemsInput.tsx b/src/components/inputs/reactHookForm/DirectoryItemsInput.tsx index 5f6a253cb..acbb553aa 100644 --- a/src/components/inputs/reactHookForm/DirectoryItemsInput.tsx +++ b/src/components/inputs/reactHookForm/DirectoryItemsInput.tsx @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Chip, FormControl, Grid, IconButton, Theme, Tooltip } from '@mui/material'; +import { Chip, FormControl, Grid, IconButton, Tooltip } from '@mui/material'; import FolderIcon from '@mui/icons-material/Folder'; import { useCallback, useMemo, useState } from 'react'; import { useController, useFieldArray } from 'react-hook-form'; @@ -18,7 +18,7 @@ import { isFieldRequired } from './utils/functions'; import ErrorInput from './errorManagement/ErrorInput'; import { useSnackMessage } from '../../../hooks/useSnackMessage'; import { TreeViewFinderNodeProps } from '../../treeViewFinder'; -import { mergeSx } from '../../../utils/styles'; +import { mergeSx, MuiStyles } from '../../../utils/styles'; import OverflowableText from '../../overflowableText'; import MidFormError from './errorManagement/MidFormError'; import DirectoryItemSelector from '../../directoryItemSelector/DirectoryItemSelector'; @@ -37,7 +37,7 @@ const styles = { borderRadius: '4px', overflow: 'hidden', }, - formDirectoryElementsError: (theme: Theme) => ({ + formDirectoryElementsError: (theme) => ({ borderColor: theme.palette.error.main, }), formDirectoryElements2: { @@ -52,7 +52,7 @@ const styles = { addDirectoryElements: { marginTop: '-5px', }, -}; +} as const satisfies MuiStyles; export interface DirectoryItemsInputProps { label: string | undefined; diff --git a/src/components/inputs/reactHookForm/agGridTable/CustomAgGridTable.tsx b/src/components/inputs/reactHookForm/agGridTable/CustomAgGridTable.tsx index 5b598f904..183931ea3 100644 --- a/src/components/inputs/reactHookForm/agGridTable/CustomAgGridTable.tsx +++ b/src/components/inputs/reactHookForm/agGridTable/CustomAgGridTable.tsx @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { AgGridReact } from 'ag-grid-react'; import 'ag-grid-community/styles/ag-grid.css'; @@ -15,6 +15,7 @@ import { useIntl } from 'react-intl'; import { CellEditingStoppedEvent, ColumnState, SortChangedEvent } from 'ag-grid-community'; import BottomRightButtons from './BottomRightButtons'; import FieldConstants from '../../../../utils/constants/fieldConstants'; +import type { MuiStyle, SxPropsObj } from '../../../../utils/styles'; export const ROW_DRAGGING_SELECTION_COLUMN_DEF = [ { @@ -25,8 +26,9 @@ export const ROW_DRAGGING_SELECTION_COLUMN_DEF = [ }, ]; -const style = (customProps: any) => ({ - grid: (theme: any) => ({ +const style = + (customProps: SxPropsObj = {}): MuiStyle => + (theme) => ({ width: 'auto', height: '100%', position: 'relative', @@ -36,10 +38,10 @@ const style = (customProps: any) => ({ // https://www.ag-grid.com/react-data-grid/global-style-customisation/ '--ag-alpine-active-color': `${theme.palette.primary.main} !important`, '--ag-checkbox-indeterminate-color': `${theme.palette.primary.main} !important`, - '--ag-background-color': `${theme.agGridBackground.color} !important`, - '--ag-header-background-color': `${theme.agGridBackground.color} !important`, - '--ag-odd-row-background-color': `${theme.agGridBackground.color} !important`, - '--ag-modal-overlay-background-color': `${theme.agGridBackground.color} !important`, + '--ag-background-color': `${theme.aggrid.background.color} !important`, + '--ag-header-background-color': `${theme.aggrid.background.color} !important`, + '--ag-odd-row-background-color': `${theme.aggrid.background.color} !important`, + '--ag-modal-overlay-background-color': `${theme.aggrid.background.color} !important`, '--ag-selected-row-background-color': 'transparent !important', '--ag-range-selection-border-color': 'transparent !important', @@ -71,24 +73,23 @@ const style = (customProps: any) => ({ height: '100%', border: 'inherit', outline: 'inherit', - backgroundColor: theme.agGridBackground.color, + backgroundColor: theme.aggrid.background.color, }, '& .Mui-focused .MuiOutlinedInput-root': { // borders moves row height outline: 'var(--ag-borders-input) var(--ag-input-focus-border-color)', outlineOffset: '-1px', - backgroundColor: theme.agGridBackground.color, + backgroundColor: theme.aggrid.background.color, }, ...customProps, - }), -}); + }); export interface CustomAgGridTableProps { name: string; columnDefs: any; makeDefaultRowData: any; csvProps: unknown; - cssProps: unknown; + cssProps?: SxPropsObj; defaultColDef: unknown; pagination: boolean; paginationPageSize: number; @@ -110,8 +111,9 @@ function CustomAgGridTable({ alwaysShowVerticalScroll, stopEditingWhenCellsLoseFocus, ...props -}: CustomAgGridTableProps) { - const theme: any = useTheme(); +}: Readonly) { + const theme = useTheme(); + const styles = useMemo(() => style(cssProps), [cssProps]); const [gridApi, setGridApi] = useState(null); const [selectedRows, setSelectedRows] = useState([]); const [newRowAdded, setNewRowAdded] = useState(false); @@ -236,7 +238,7 @@ function CustomAgGridTable({ return ( - + ({ + inputLegend: (theme) => ({ backgroundImage: 'linear-gradient(rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.16))', backgroundColor: theme.palette.background.paper, padding: '0 8px 0 8px', }), -}; +} as const satisfies MuiStyles; export const RangeType = { EQUALITY: { id: 'EQUALITY', label: 'equality' }, @@ -30,13 +29,14 @@ export const RangeType = { LESS_THAN: { id: 'LESS_THAN', label: 'lessThan' }, LESS_OR_EQUAL: { id: 'LESS_OR_EQUAL', label: 'lessOrEqual' }, RANGE: { id: 'RANGE', label: 'range' }, -}; +} as const; export const DEFAULT_RANGE_VALUE = { [FieldConstants.OPERATION_TYPE]: RangeType.EQUALITY.id, [FieldConstants.VALUE_1]: null, [FieldConstants.VALUE_2]: null, -}; +} as const; + export const getRangeInputDataForm = (name: string, rangeValue: unknown) => ({ [name]: rangeValue, }); @@ -65,7 +65,7 @@ interface RangeInputProps { label: string; } -function RangeInput({ name, label }: RangeInputProps) { +function RangeInput({ name, label }: Readonly) { const watchOperationType = useWatch({ name: `${name}.${FieldConstants.OPERATION_TYPE}`, }); diff --git a/src/components/inputs/reactHookForm/text/ExpandingTextField.tsx b/src/components/inputs/reactHookForm/text/ExpandingTextField.tsx index cb99e64f1..3cd43eb69 100644 --- a/src/components/inputs/reactHookForm/text/ExpandingTextField.tsx +++ b/src/components/inputs/reactHookForm/text/ExpandingTextField.tsx @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { useState } from 'react'; -import { TextFieldProps, Theme, Typography } from '@mui/material'; +import { useCallback, useState } from 'react'; +import { TextFieldProps, Typography } from '@mui/material'; import { useWatch } from 'react-hook-form'; import useCustomFormContext from '../provider/useCustomFormContext'; import TextInput, { TextInputProps } from './TextInput'; @@ -37,20 +37,17 @@ function ExpandingTextField({ name, control, }); - const handleFocus = () => { + const handleFocus = useCallback(() => { setIsFocused(true); - }; - - const handleBlur = () => { + }, []); + const handleBlur = useCallback(() => { setIsFocused(false); - }; + }, []); const isOverTheLimit = descriptionWatch?.length > maxCharactersNumber; const descriptionLength = descriptionWatch?.length ?? 0; const descriptionCounter = `${descriptionLength}/${maxCharactersNumber}`; - const rowsToDisplay = isFocused ? rows : minRows; - - const formProps = { + const formProps: TextInputProps['formProps'] = { size: 'medium', multiline: true, onFocus: handleFocus, @@ -67,7 +64,7 @@ function ExpandingTextField({ FormHelperTextProps: { sx: { ml: 'auto', - color: (theme: Theme) => (isOverTheLimit ? theme.palette.error.main : theme.palette.text.secondary), + color: (theme) => (isOverTheLimit ? theme.palette.error.main : theme.palette.text.secondary), }, }, ...(rowsToDisplay && { rows: rowsToDisplay }), diff --git a/src/components/inputs/reactHookForm/utils/TextFieldWithAdornment.tsx b/src/components/inputs/reactHookForm/utils/TextFieldWithAdornment.tsx index cdb4a77da..3f33bc5ba 100644 --- a/src/components/inputs/reactHookForm/utils/TextFieldWithAdornment.tsx +++ b/src/components/inputs/reactHookForm/utils/TextFieldWithAdornment.tsx @@ -7,8 +7,7 @@ import { useCallback, useState } from 'react'; import { Clear as ClearIcon } from '@mui/icons-material'; -import { IconButton, InputAdornment, TextField, TextFieldProps } from '@mui/material'; - +import { IconButton, InputAdornment, TextField, TextFieldProps, TextFieldVariants } from '@mui/material'; import { Input } from '../../../../utils/types/types'; export type TextFieldWithAdornmentProps = TextFieldProps & { @@ -24,7 +23,7 @@ function TextFieldWithAdornment(props: TextFieldWithAdornmentProps) { const [isFocused, setIsFocused] = useState(false); - const getAdornmentStyle = useCallback((innerVariant: 'standard' | 'filled' | 'outlined') => { + const getAdornmentStyle = useCallback((innerVariant: TextFieldVariants) => { if (innerVariant === 'filled') { return { alignItems: 'start', diff --git a/src/components/inputs/reactQueryBuilder/AutocompleteWithFavorites.tsx b/src/components/inputs/reactQueryBuilder/AutocompleteWithFavorites.tsx index c9099e483..889ebddb2 100644 --- a/src/components/inputs/reactQueryBuilder/AutocompleteWithFavorites.tsx +++ b/src/components/inputs/reactQueryBuilder/AutocompleteWithFavorites.tsx @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Autocomplete, Box, TextField, Theme } from '@mui/material'; +import { Autocomplete, Box, TextField } from '@mui/material'; import { useMemo } from 'react'; import { AutocompleteProps } from '@mui/material/Autocomplete/Autocomplete'; +import { MuiStyles } from '../../../utils/styles'; const styles = { - favBox: (theme: Theme) => ({ + favBox: (theme) => ({ borderBottom: '1px solid', borderColor: theme.palette.divider, }), -}; +} as const satisfies MuiStyles; interface AutocompleteWithFavoritesProps extends Omit< @@ -52,7 +53,7 @@ function AutocompleteWithFavorites({ options={optionsWithFavorites} {...otherProps} /* props should not be overridden */ - groupBy={(option: Value) => (favorites.includes(option) ? `fav` : 'not_fav')} + groupBy={(option: Value) => (favorites.includes(option) ? 'fav' : 'not_fav')} multiple={Array.isArray(value)} renderInput={(params) => } renderGroup={(item) => { diff --git a/src/components/inputs/reactQueryBuilder/ValueEditor.tsx b/src/components/inputs/reactQueryBuilder/ValueEditor.tsx index d9311adb5..9ce227b7e 100644 --- a/src/components/inputs/reactQueryBuilder/ValueEditor.tsx +++ b/src/components/inputs/reactQueryBuilder/ValueEditor.tsx @@ -23,6 +23,7 @@ import PropertyValueEditor from './PropertyValueEditor'; import { FilterType } from '../../filter/constants/FilterConstants'; import GroupValueEditor from './compositeRuleEditor/GroupValueEditor'; import { OPERATOR_OPTIONS } from '../../filter/expert/expertFilterConstants'; +import { MuiStyles } from '../../../utils/styles'; const styles = { noArrows: { @@ -33,7 +34,7 @@ const styles = { MozAppearance: 'textfield', }, }, -}; +} as const satisfies MuiStyles; function ValueEditor(props: ValueEditorProps) { const { field, operator, value, rule, handleOnChange, inputType, fieldData } = props; diff --git a/src/components/inputs/reactQueryBuilder/compositeRuleEditor/GroupValueEditor.tsx b/src/components/inputs/reactQueryBuilder/compositeRuleEditor/GroupValueEditor.tsx index 7d6378497..78801f844 100644 --- a/src/components/inputs/reactQueryBuilder/compositeRuleEditor/GroupValueEditor.tsx +++ b/src/components/inputs/reactQueryBuilder/compositeRuleEditor/GroupValueEditor.tsx @@ -5,18 +5,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { ValueEditorProps } from 'react-querybuilder'; -import { Grid, Theme } from '@mui/material'; +import { Grid } from '@mui/material'; import { useCallback } from 'react'; import RuleValueEditor from './RuleValueEditor'; import { CompositeField, CompositeGroup, CompositeRule } from '../../../filter/expert/expertFilter.type'; +import { MuiStyles } from '../../../../utils/styles'; const styles = { - group: (theme: Theme) => ({ + group: (theme) => ({ border: 1, borderRadius: 1, borderColor: theme.palette.grey[500], }), -}; +} as const satisfies MuiStyles; function GroupValueEditor(props: ValueEditorProps) { const { diff --git a/src/components/inputs/reactQueryBuilder/compositeRuleEditor/RuleValueEditor.tsx b/src/components/inputs/reactQueryBuilder/compositeRuleEditor/RuleValueEditor.tsx index 0179d0206..893084825 100644 --- a/src/components/inputs/reactQueryBuilder/compositeRuleEditor/RuleValueEditor.tsx +++ b/src/components/inputs/reactQueryBuilder/compositeRuleEditor/RuleValueEditor.tsx @@ -8,6 +8,7 @@ import { ValueEditorProps } from 'react-querybuilder'; import { Grid, MenuItem, Select, Typography } from '@mui/material'; import { useIntl } from 'react-intl'; import { CompositeRule, OperatorOption } from '../../../filter/expert/expertFilter.type'; +import { MuiStyles } from '../../../../utils/styles'; const styles = { gridItem: { @@ -15,7 +16,7 @@ const styles = { justifyContent: 'flex-end', alignItems: 'baseline', }, -}; +} as const satisfies MuiStyles; type RuleValueEditorProps = ValueEditorProps & { rule?: CompositeRule; diff --git a/src/components/muiVirtualizedTable/ColumnHeader.tsx b/src/components/muiVirtualizedTable/ColumnHeader.tsx index 5517ee97e..683cb8017 100644 --- a/src/components/muiVirtualizedTable/ColumnHeader.tsx +++ b/src/components/muiVirtualizedTable/ColumnHeader.tsx @@ -6,16 +6,13 @@ */ import { ComponentProps, forwardRef, MouseEvent, ReactNode, useCallback, useMemo, useRef, useState } from 'react'; - import { ArrowDownward as ArrowDownwardIcon, ArrowUpward as ArrowUpwardIcon, FilterAltOutlined as FilterAltOutlinedIcon, } from '@mui/icons-material'; - -import { styled } from '@mui/system'; -import { Box, BoxProps, Theme } from '@mui/material'; -import { mergeSx } from '../../utils/styles'; +import { Box, BoxProps, styled } from '@mui/material'; +import { mergeSx, MuiStyles } from '../../utils/styles'; const styles = { label: { @@ -43,7 +40,7 @@ const styles = { filterButton: { stroke: 'currentcolor', }, - filterTooLossy: (theme: Theme) => ({ + filterTooLossy: (theme) => ({ stroke: theme.palette.secondary.main, }), transparent: { @@ -52,7 +49,7 @@ const styles = { hovered: { opacity: 0.5, }, -}; +} as const satisfies MuiStyles; interface SortButtonProps { signedRank?: number; diff --git a/src/components/muiVirtualizedTable/MuiVirtualizedTable.tsx b/src/components/muiVirtualizedTable/MuiVirtualizedTable.tsx index d80069fc5..237e33073 100644 --- a/src/components/muiVirtualizedTable/MuiVirtualizedTable.tsx +++ b/src/components/muiVirtualizedTable/MuiVirtualizedTable.tsx @@ -8,17 +8,16 @@ /** * This class has been taken from 'Virtualized Table' example at https://material-ui.com/components/tables/ */ -import { createRef, PureComponent, ReactElement, ReactNode, MouseEvent, KeyboardEvent, MutableRefObject } from 'react'; +import { createRef, KeyboardEvent, MouseEvent, MutableRefObject, PureComponent, ReactElement, ReactNode } from 'react'; import { FormattedMessage } from 'react-intl'; import clsx from 'clsx'; import memoize from 'memoize-one'; -import { Autocomplete, Chip, IconButton, Popover, SxProps, TableCell, TextField } from '@mui/material'; -import { styled } from '@mui/system'; +import { Autocomplete, Chip, IconButton, Popover, styled, TableCell, TextField } from '@mui/material'; import { GetApp as GetAppIcon } from '@mui/icons-material'; import { AutoSizer, Column, ColumnProps, RowMouseEventHandlerParams, Table, TableCellProps } from 'react-virtualized'; import CsvDownloader from 'react-csv-downloader'; import { OverflowableText } from '../overflowableText/OverflowableText'; -import { makeComposeClasses, toNestedGlobalSelectors } from '../../utils/styles'; +import { makeComposeClasses, MuiStyle, MuiStyles, SxPropsObj, toNestedGlobalSelectors } from '../../utils/styles'; import { ChangeWays, collectibleHelper, getHelper, KeyedColumnsRowIndexer } from './KeyedColumnsRowIndexer'; import { ColumnHeader } from './ColumnHeader'; @@ -66,7 +65,7 @@ const defaultStyles = { '& .ReactVirtualized__Table__headerRow': { flip: false, }, - }, + } as MuiStyle, // "flip" isn't recognized property [cssTableRow]: { cursor: 'pointer', }, @@ -84,7 +83,7 @@ const defaultStyles = { }, [cssRowBackgroundDark]: {}, [cssRowBackgroundLight]: {}, -}; +} as const satisfies MuiStyles; // TODO find a better system, maybe MUI will fix/improve this ? // Different from all others because of the poor nested sx support for portals @@ -262,7 +261,7 @@ export interface MuiVirtualizedTableProps extends CustomColumnProps { onRowClick?: (event: RowMouseEventHandlerParams) => void; rowHeight: number; onCellClick: (row: RowProps, column: ColumnProps) => void; - tooltipSx: SxProps; + tooltipSx: SxPropsObj; name: string; exportCSVDataKeys: unknown[]; enableExportCSV: boolean; diff --git a/src/components/overflowableText/OverflowableText.tsx b/src/components/overflowableText/OverflowableText.tsx index 78847d727..b2cad5782 100644 --- a/src/components/overflowableText/OverflowableText.tsx +++ b/src/components/overflowableText/OverflowableText.tsx @@ -5,8 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { ReactElement, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { Box, BoxProps, SxProps, Theme, Tooltip, styled } from '@mui/material'; +import { Box, BoxProps, styled, Tooltip } from '@mui/material'; import { Style } from 'node:util'; +import { MuiStyle, MuiStyles } from '../../utils/styles'; const overflowStyle = { overflow: { @@ -20,9 +21,9 @@ const overflowStyle = { width: 'fit-content', maxWidth: 'fit-content', }, -}; +} as const satisfies MuiStyles; -const multilineOverflowStyle = (numberOfLinesToDisplay?: number): SxProps => ({ +const multilineOverflowStyle = (numberOfLinesToDisplay?: number): MuiStyle => ({ overflow: 'hidden', display: '-webkit-box', WebkitLineClamp: numberOfLinesToDisplay /* number of lines to show */, @@ -34,15 +35,15 @@ const multilineOverflowStyle = (numberOfLinesToDisplay?: number): SxProps => ({ export interface OverflowableTextProps extends BoxProps { text?: ReactElement | string; maxLineCount?: number; - tooltipStyle?: Style; - tooltipSx?: SxProps; + tooltipStyle?: Style; // TODO can be only boolean + tooltipSx?: MuiStyle; } export const OverflowableText = styled( ({ text, maxLineCount, // overflowable text can be displayed on several lines if this is set to a number > 1 tooltipStyle, - tooltipStyle, + tooltipStyle, // TODO: why not just do "tooltipSx!==undefined"? tooltipSx, className, children, diff --git a/src/components/topBar/AboutDialog.tsx b/src/components/topBar/AboutDialog.tsx index e787f5756..772ab3ac8 100644 --- a/src/components/topBar/AboutDialog.tsx +++ b/src/components/topBar/AboutDialog.tsx @@ -22,7 +22,6 @@ import { Fade, Grid, Stack, - Theme, Tooltip, tooltipClasses, Typography, @@ -34,6 +33,7 @@ import { LoadingButton } from '@mui/lab'; import { Apps, DnsOutlined, ExpandMore, Gavel, QuestionMark, Refresh, WidgetsOutlined } from '@mui/icons-material'; import { FormattedMessage } from 'react-intl'; import { LogoText } from './GridLogo'; +import { MuiStyles } from '../../utils/styles'; const styles = { general: { @@ -52,21 +52,19 @@ const styles = { textAlign: 'center', marginTop: 0, }, - versionField: (isUnknown: boolean) => - isUnknown - ? { - fontSize: '1.5em', - fontWeight: 'bold', - } - : { - fontStyle: 'italic', - }, + versionField: { + fontStyle: 'italic', + }, + versionFieldUnknown: { + fontSize: '1.5em', + fontWeight: 'bold', + }, detailsSection: { '.MuiAccordionSummary-content > .MuiSvgIcon-root': { marginRight: '0.5rem', }, }, -}; +} as const satisfies MuiStyles; function getGlobalVersion( fnPromise: (() => Promise) | undefined, @@ -154,7 +152,7 @@ const moduleStyles = { alignSelf: 'flex-end', flexShrink: 0, }, - tooltip: (theme: Theme) => ({ + tooltip: (theme) => ({ [`& .${tooltipClasses.tooltip}`]: { border: '1px solid #dadde9', boxShadow: theme.shadows[1], @@ -175,7 +173,7 @@ const moduleStyles = { paddingLeft: '0.5em', }, }, -}; +} as const satisfies MuiStyles; const ModuleTypesIcons = { app: , @@ -411,7 +409,11 @@ function AboutDialog({ version: ( {actualGlobalVersion || 'unknown'} diff --git a/src/components/topBar/GridLogo.tsx b/src/components/topBar/GridLogo.tsx index 1eec78e94..1fda56d81 100644 --- a/src/components/topBar/GridLogo.tsx +++ b/src/components/topBar/GridLogo.tsx @@ -5,10 +5,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Box, SxProps, Typography } from '@mui/material'; +import { Box, Typography } from '@mui/material'; import { BrokenImage } from '@mui/icons-material'; import { ReactNode } from 'react'; -import { mergeSx } from '../../utils/styles'; +import { mergeSx, MuiStyle, MuiStyles } from '../../utils/styles'; const styles = { logo: { @@ -24,7 +24,7 @@ const styles = { clickable: { cursor: 'pointer', }, -}; +} as const satisfies MuiStyles; export interface GridLogoProps extends Omit { appLogo: ReactNode; @@ -55,6 +55,6 @@ export default GridLogo; export interface LogoTextProps { appName: string; appColor: string; - style: SxProps; + style: MuiStyle; onClick: () => void; } diff --git a/src/components/topBar/TopBar.tsx b/src/components/topBar/TopBar.tsx index 8b3306d2e..12e064a0f 100644 --- a/src/components/topBar/TopBar.tsx +++ b/src/components/topBar/TopBar.tsx @@ -23,7 +23,7 @@ import { MenuProps, Paper, Popper, - Theme, + styled, ToggleButton, ToggleButtonGroup, Toolbar, @@ -41,13 +41,12 @@ import { Settings as SettingsIcon, WbSunny as WbSunnyIcon, } from '@mui/icons-material'; -import { styled } from '@mui/system'; - import { User } from 'oidc-client'; import GridLogo, { GridLogoProps } from './GridLogo'; import AboutDialog, { AboutDialogProps } from './AboutDialog'; import { LogoutProps } from '../authentication/Logout'; import { CommonMetadata } from '../../services'; +import { MuiStyles } from '../../utils/styles'; const styles = { grow: { @@ -62,7 +61,7 @@ const styles = { textDecoration: 'none', color: 'inherit', }, - name: (theme: Theme) => ({ + name: (theme) => ({ backgroundColor: darken(theme.palette.background.paper, 0.1), paddingTop: '10px', borderRadius: '100%', @@ -109,7 +108,7 @@ const styles = { height: '30px', width: '48px', }, -}; +} as const satisfies MuiStyles; const StyledMenu = styled((props: MenuProps) => ( `GsiTreeViewFinder-${className}`; const composeClasses = makeComposeClasses(generateTreeViewFinderClass); @@ -207,7 +204,7 @@ function TreeViewFinder(props: TreeViewFinderProps) { }); }; - const handleNodeToggle = (_e: React.SyntheticEvent, nodeIds: string[]) => { + const handleNodeToggle = (_e: SyntheticEvent, nodeIds: string[]) => { // onTreeBrowse proc only on last node clicked and only when expanded nodeIds.every((nodeId) => { if (!expanded?.includes(nodeId)) { @@ -263,7 +260,7 @@ function TreeViewFinder(props: TreeViewFinderProps) { }, [expanded, selectedProp, expandedProp, data, autoScrollAllowed]); /* User Interaction management */ - const handleNodeSelect = (_e: React.SyntheticEvent, values: string | string[]) => { + const handleNodeSelect = (_e: SyntheticEvent, values: string | string[]) => { // Default management if (multiSelect && Array.isArray(values)) { setSelected(values.filter((nodeId) => isSelectable(mapPrintedNodes[nodeId]))); diff --git a/src/index.ts b/src/index.ts index 926e77e66..d2ec8dc0b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -272,7 +272,9 @@ export { getCriteriaBasedSchema, } from './components/filter/criteriaBased/criteriaBasedFilterUtils'; +export type { CommonMuiTheme, CommonMuiPalette } from './module-mui'; export { mergeSx } from './utils/styles'; +export type { MuiStyle, MuiStyles } from './utils/styles'; export { setCommonStore } from './redux/commonStore'; export type { CommonStoreState } from './redux/commonStore'; diff --git a/src/module-emotion.d.ts b/src/module-emotion.d.ts new file mode 100644 index 000000000..0a217ee5d --- /dev/null +++ b/src/module-emotion.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright © 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +// https://github.com/emotion-js/emotion/discussions/2291 +// https://emotion.sh/docs/typescript#define-a-theme + +// import { Theme as EmotionTheme } from '@emotion/react'; +import { Theme as MaterialUITheme } from '@mui/material'; + +declare module '@emotion/react' { + // Re-declare the emotion theme to have the properties of the MaterialUiTheme + // eslint-disable-next-line spaced-comment + export interface Theme extends /*EmotionTheme,*/ MaterialUITheme {} +} diff --git a/src/module-mui.d.ts b/src/module-mui.d.ts index eb2d10376..a5768a4f5 100644 --- a/src/module-mui.d.ts +++ b/src/module-mui.d.ts @@ -1,21 +1,45 @@ -/** - * Copyright (c) 2024, RTE (http://www.rte-france.com) +/* + * Copyright © 2024, RTE (http://www.rte-france.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* eslint-disable spaced-comment */ +//import { Theme as MuiTheme, ThemeOptions as MuiThemeOptions } from '@mui/material/styles/createTheme'; +//import { Palette as MuiPalette, PaletteOptions as MuiPaletteOptions } from '@mui/material/styles/createPalette'; +import type { CSSObject } from '@mui/styled-engine'; +import type { Property } from 'csstype'; + +export type CommonMuiTheme = { + aggrid: { + theme: 'ag-theme-alpine' | 'ag-theme-alpine-dark'; + highlightColor: Property.Color; + valueChangeHighlightBackgroundColor: Property.Color; + overlay: { + background: CSSObject; + }; + background: CSSObject; + }; +}; + +export type CommonMuiPalette = {}; + // used to customize mui theme // https://mui.com/material-ui/customization/theming/#typescript -declare module '@mui/material/styles/createTheme' { - interface Theme { - aggrid: { - theme: string; - highlightColor: string; - valueChangeHighlightBackgroundColor: string; - overlay: { - background: string; - }; - }; - } +declare module '@mui/material/styles' { + // export * from '@mui/material/styles'; + + export interface Theme extends /*MuiTheme,*/ Required {} + // allow configuration using `createTheme` + export interface ThemeOptions extends /*MuiThemeOptions,*/ Partial {} + + export interface Palette extends /*MuiPalette,*/ Required {} + // allow configuration using `createPalette` + export interface PaletteOptions extends /*MuiPaletteOptions,*/ Partial {} +} + +declare module '@mui/utils/capitalize' { + // override to have result prediction + export default function capitalize(string: S): Capitalize; } diff --git a/src/utils/mapper/elementIcon.tsx b/src/utils/mapper/elementIcon.tsx index 0ac7218b9..7ba734469 100644 --- a/src/utils/mapper/elementIcon.tsx +++ b/src/utils/mapper/elementIcon.tsx @@ -12,10 +12,10 @@ import { PhotoLibrary as PhotoLibraryIcon, Settings as SettingsIcon, } from '@mui/icons-material'; -import { SxProps, Theme } from '@mui/material'; import { ElementType } from '../types/elementType'; +import { MuiStyle } from '../styles'; -function getFileIcon(type: ElementType, style: SxProps) { +function getFileIcon(type: ElementType, style: MuiStyle) { switch (type) { case ElementType.STUDY: return ; diff --git a/src/utils/styles.ts b/src/utils/styles.ts index ed86480ab..d051fc1d7 100644 --- a/src/utils/styles.ts +++ b/src/utils/styles.ts @@ -5,6 +5,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { SxProps, Theme } from '@mui/material'; +import { SystemStyleObject } from '@mui/system/styleFunctionSx/styleFunctionSx'; + +export type MuiStyle = SxProps; +export type MuiStyles = Record>; + +/** + * Same Mui's SxProps, but without the array version in possibilities. + */ +export type SxPropsObj = SystemStyleObject | ((theme: Theme) => SystemStyleObject); // TODO do we need to export this to clients (index.ts) ? // like mui sx(slot)/class merging but simpler with less features @@ -16,11 +25,11 @@ export const makeComposeClasses = export const toNestedGlobalSelectors = (styles: object, generateGlobalClass: (className: string) => string) => Object.fromEntries(Object.entries(styles).map(([k, v]) => [`& .${generateGlobalClass(k)}`, v])); -const isSxProps = (sx: SxProps | undefined): sx is SxProps => { +const isSxProps = (sx: MuiStyle | undefined): sx is SxProps => { return sx !== undefined; }; // https://mui.com/system/getting-started/the-sx-prop/#passing-the-sx-prop // You cannot spread or concat directly because `SxProps` (typeof sx) can be an array. */ // same as [{}, ...(Array.isArray(sx) ? sx : [sx])] -export const mergeSx = (...allSx: (SxProps | undefined)[]) => allSx.filter(isSxProps).flat(); +export const mergeSx = (...allSx: (MuiStyle | undefined)[]) => allSx.filter(isSxProps).flat(); diff --git a/src/utils/types/equipmentType.ts b/src/utils/types/equipmentType.ts index 5ce764653..2b8193c5c 100644 --- a/src/utils/types/equipmentType.ts +++ b/src/utils/types/equipmentType.ts @@ -7,6 +7,7 @@ import { Theme } from '@mui/material'; import { LIGHT_THEME } from '../../components/topBar/TopBar'; +import { MuiStyles } from '../styles'; export const TYPE_TAG_MAX_SIZE = '90px'; export const VL_TAG_MAX_SIZE = '100px'; @@ -48,7 +49,7 @@ export const equipmentStyles = { width: '100%', padding: '2px', }, -}; +} as const satisfies MuiStyles; /** * The order of the equipments in this list is important, as many UI follow it directly. From 3ff7a4b09f4cb1df258b1d0e52152bc5f697755c Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Wed, 18 Sep 2024 12:15:48 +0200 Subject: [PATCH 2/8] TS module augmentation now available to front apps --- README.md | 36 ++++++++++++++++++++++++------------ package.json | 26 ++++++++++++++++++++------ src/index.ts | 1 - src/module-mui.d.ts | 4 ++-- vite.config.mts | 2 +- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 20733a8b7..69bb6550c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,22 @@ Library for sharing GridSuite apps commons components -#### For developers + +## Use and integration +### MUI & Emotion styles +Some components of commons-ui define custom rules in Mui styling system that need +to be initialized. +To have TypeScript helping (through module augmentation), you can in a `.d.ts` +file at the root of your project source folder (like `globals.d.ts`): +```ts +/// +/// +``` +Typescript will check the new options with function related to `Theme` interface. + + +## Build & deploy +### For developers The commons-ui library have a demo app in which you can call your components to test them. The `npm start` command install the library's dependencies then launches the demo app. @@ -13,10 +28,9 @@ to build commons-ui via - `npm run build:pack` Then in the my-app project : -- Change the commons-ui dependency in my-app's package.json from -`@gridsuite/commons-ui:"^x.x.x"` -to -`@gridsuite/commons-ui:"file:{PATH_TO_LIBRARY}/gridsuite-commons-ui-{LIBRARY_VERSION}.tgz"` +- Change the commons-ui dependency in my-app's `package.json` + from`@gridsuite/commons-ui:"^x.x.x"` + to `@gridsuite/commons-ui:"file:{PATH_TO_LIBRARY}/gridsuite-commons-ui-{LIBRARY_VERSION}.tgz"` - `npm install` - `npm start` @@ -25,11 +39,10 @@ To fix this, run this command from the app **after** running "npm install" - rm -Rf node_modules/.cache -#### For integrators +### For integrators If you want to deploy a new version of commons-ui in the [NPM package registry](https://www.npmjs.com/package/@gridsuite/commons-ui), you need to follow the steps below: - - Update to the new version in [package.json](https://github.com/gridsuite/commons-ui/blob/main/package.json) (example `0.6.0`) - Build it: `npm install` - Commit the package.json and package-lock.json files, push to a branch, make a PR, have it reviewed and merged to main. @@ -40,14 +53,13 @@ you need to follow the steps below: - Click on "Publish release" - It will trigger a job that will publish the release on NPM -#### License Headers and dependencies checking +### License Headers and dependencies checking To check dependencies license compatibility with this project one locally, please run the following command : - -``` +```shell npm run licenses-check ``` -Notes : -* Check [license-checker-config.json](license-checker-config.json) for license white list and exclusion. +Notes: +Check [license-checker-config.json](license-checker-config.json) for license white list and exclusion. If you need to update this list, please inform organization's owners. diff --git a/package.json b/package.json index a1dccb29e..ebe8093be 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,30 @@ { "name": "@gridsuite/commons-ui", "version": "0.65.1", - "description": "common react components for gridsuite applications", + "description": "Common React components for GridSuite applications", "engines": { "npm": ">=9", "node": ">=18" }, - "exports": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./module-mui": { + "types": "./src/module-mui.d.ts" + }, + "./module-emotion": { + "types": "./src/module-emotion.d.ts" + }, + "./package.json": "./package.json" + }, "files": [ - "dist" + "dist", + "src/module-mui.d.ts", + "src/module-emotion.d.ts" ], "sideEffects": [ "**/*.css" @@ -46,18 +60,18 @@ "uuid": "^9.0.1" }, "peerDependencies": { - "@mui/system": "^5.15.15", - "@mui/x-tree-view": "^6.17.0", - "papaparse": "^5.4.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "5.0.0-alpha.169", "@mui/material": "^5.15.14", + "@mui/system": "^5.15.15", + "@mui/x-tree-view": "^6.17.0", "ag-grid-community": "^31.0.0", "ag-grid-react": "^31.2.0", "notistack": "^3.0.1", + "papaparse": "^5.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.2", diff --git a/src/index.ts b/src/index.ts index d2ec8dc0b..ac686c5d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -272,7 +272,6 @@ export { getCriteriaBasedSchema, } from './components/filter/criteriaBased/criteriaBasedFilterUtils'; -export type { CommonMuiTheme, CommonMuiPalette } from './module-mui'; export { mergeSx } from './utils/styles'; export type { MuiStyle, MuiStyles } from './utils/styles'; export { setCommonStore } from './redux/commonStore'; diff --git a/src/module-mui.d.ts b/src/module-mui.d.ts index a5768a4f5..fccdd23e6 100644 --- a/src/module-mui.d.ts +++ b/src/module-mui.d.ts @@ -11,7 +11,7 @@ import type { CSSObject } from '@mui/styled-engine'; import type { Property } from 'csstype'; -export type CommonMuiTheme = { +type CommonMuiTheme = { aggrid: { theme: 'ag-theme-alpine' | 'ag-theme-alpine-dark'; highlightColor: Property.Color; @@ -23,7 +23,7 @@ export type CommonMuiTheme = { }; }; -export type CommonMuiPalette = {}; +type CommonMuiPalette = {}; // used to customize mui theme // https://mui.com/material-ui/customization/theming/#typescript diff --git a/vite.config.mts b/vite.config.mts index 323224e8a..b586c8af8 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -43,7 +43,7 @@ export default defineConfig((config) => ({ // from https://rollupjs.org/configuration-options/#input input: Object.fromEntries( globSync('src/**/*.{js,jsx,ts,tsx}', { - ignore: ['src/vite-env.d.ts', 'src/**/*.test.{js,jsx,ts,tsx}'], + ignore: ['src/vite-env.d.ts', 'src/module-*.d.ts', 'src/**/*.test.{js,jsx,ts,tsx}'], }).map((file) => [ // This remove `src/` as well as the file extension from each // file, so e.g. src/nested/foo.js becomes nested/foo From 94ffcf2c3608516eae31e38232ab8db22d1dcaa7 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Wed, 18 Sep 2024 12:14:33 +0200 Subject: [PATCH 3/8] Fix MuiStyle object definition for spread operator. --- .../reactHookForm/agGridTable/CustomAgGridTable.tsx | 6 +++--- .../muiVirtualizedTable/MuiVirtualizedTable.tsx | 4 ++-- src/index.ts | 2 +- src/utils/styles.ts | 11 ++++++++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/components/inputs/reactHookForm/agGridTable/CustomAgGridTable.tsx b/src/components/inputs/reactHookForm/agGridTable/CustomAgGridTable.tsx index 183931ea3..1aafef8e6 100644 --- a/src/components/inputs/reactHookForm/agGridTable/CustomAgGridTable.tsx +++ b/src/components/inputs/reactHookForm/agGridTable/CustomAgGridTable.tsx @@ -15,7 +15,7 @@ import { useIntl } from 'react-intl'; import { CellEditingStoppedEvent, ColumnState, SortChangedEvent } from 'ag-grid-community'; import BottomRightButtons from './BottomRightButtons'; import FieldConstants from '../../../../utils/constants/fieldConstants'; -import type { MuiStyle, SxPropsObj } from '../../../../utils/styles'; +import type { MuiStyle, MuiStyleObj } from '../../../../utils/styles'; export const ROW_DRAGGING_SELECTION_COLUMN_DEF = [ { @@ -27,7 +27,7 @@ export const ROW_DRAGGING_SELECTION_COLUMN_DEF = [ ]; const style = - (customProps: SxPropsObj = {}): MuiStyle => + (customProps: MuiStyleObj = {}): MuiStyle => (theme) => ({ width: 'auto', height: '100%', @@ -89,7 +89,7 @@ export interface CustomAgGridTableProps { columnDefs: any; makeDefaultRowData: any; csvProps: unknown; - cssProps?: SxPropsObj; + cssProps?: MuiStyleObj; defaultColDef: unknown; pagination: boolean; paginationPageSize: number; diff --git a/src/components/muiVirtualizedTable/MuiVirtualizedTable.tsx b/src/components/muiVirtualizedTable/MuiVirtualizedTable.tsx index 237e33073..d4695690b 100644 --- a/src/components/muiVirtualizedTable/MuiVirtualizedTable.tsx +++ b/src/components/muiVirtualizedTable/MuiVirtualizedTable.tsx @@ -17,7 +17,7 @@ import { GetApp as GetAppIcon } from '@mui/icons-material'; import { AutoSizer, Column, ColumnProps, RowMouseEventHandlerParams, Table, TableCellProps } from 'react-virtualized'; import CsvDownloader from 'react-csv-downloader'; import { OverflowableText } from '../overflowableText/OverflowableText'; -import { makeComposeClasses, MuiStyle, MuiStyles, SxPropsObj, toNestedGlobalSelectors } from '../../utils/styles'; +import { makeComposeClasses, MuiStyle, MuiStyles, MuiStyleObj, toNestedGlobalSelectors } from '../../utils/styles'; import { ChangeWays, collectibleHelper, getHelper, KeyedColumnsRowIndexer } from './KeyedColumnsRowIndexer'; import { ColumnHeader } from './ColumnHeader'; @@ -261,7 +261,7 @@ export interface MuiVirtualizedTableProps extends CustomColumnProps { onRowClick?: (event: RowMouseEventHandlerParams) => void; rowHeight: number; onCellClick: (row: RowProps, column: ColumnProps) => void; - tooltipSx: SxPropsObj; + tooltipSx: MuiStyleObj; name: string; exportCSVDataKeys: unknown[]; enableExportCSV: boolean; diff --git a/src/index.ts b/src/index.ts index ac686c5d2..9acdc00e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -273,7 +273,7 @@ export { } from './components/filter/criteriaBased/criteriaBasedFilterUtils'; export { mergeSx } from './utils/styles'; -export type { MuiStyle, MuiStyles } from './utils/styles'; +export type { MuiStyle, MuiStyleObj, MuiStyles } from './utils/styles'; export { setCommonStore } from './redux/commonStore'; export type { CommonStoreState } from './redux/commonStore'; diff --git a/src/utils/styles.ts b/src/utils/styles.ts index d051fc1d7..696a2e7ca 100644 --- a/src/utils/styles.ts +++ b/src/utils/styles.ts @@ -7,13 +7,18 @@ import { SxProps, Theme } from '@mui/material'; import { SystemStyleObject } from '@mui/system/styleFunctionSx/styleFunctionSx'; +/** + * Mui's {@link SxProps} preset with {@link Theme}. + */ export type MuiStyle = SxProps; +/** + * Alias for `const styles = {}` definitions. + */ export type MuiStyles = Record>; - /** - * Same Mui's SxProps, but without the array version in possibilities. + * Same Mui's SxProps, but without the array and function version in possibilities. */ -export type SxPropsObj = SystemStyleObject | ((theme: Theme) => SystemStyleObject); +export type MuiStyleObj = SystemStyleObject; // TODO do we need to export this to clients (index.ts) ? // like mui sx(slot)/class merging but simpler with less features From a7849828bb8c66878be3da49741d445087f39104 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Tue, 7 Jan 2025 18:37:27 +0100 Subject: [PATCH 4/8] update new code from latest version --- demo/src/app.jsx | 15 +++++---- .../checkBoxList/ClickableRowItem.tsx | 3 +- .../customMuiDialog/CustomMuiDialog.tsx | 2 +- .../reactHookForm/numbers/RangeInput.tsx | 4 +-- .../utils/TextFieldWithAdornment.tsx | 31 +++++++++---------- .../MuiVirtualizedTable.tsx | 10 +++--- .../overflowableText/OverflowableText.tsx | 30 +++++++++--------- .../snackbarProvider/SnackbarProvider.tsx | 11 +++---- .../topBar/UserInformationDialog.tsx | 3 +- src/utils/styles.ts | 2 ++ 10 files changed, 59 insertions(+), 52 deletions(-) diff --git a/demo/src/app.jsx b/demo/src/app.jsx index 3579c85bd..8b7c96d00 100644 --- a/demo/src/app.jsx +++ b/demo/src/app.jsx @@ -175,7 +175,7 @@ const getMuiTheme = (theme) => { return darkTheme; }; -const style = { +const styles = { button: { float: 'left', margin: '5px', @@ -196,6 +196,9 @@ const TreeViewFinderCustomStyles = (theme) => ({ }, }); +/** + * @param {import('@mui/material/styles').Theme} theme Theme from ThemeProvider + */ const TreeViewFinderCustomStylesEmotion = ({ theme }) => toNestedGlobalSelectors(TreeViewFinderCustomStyles(theme), generateTreeViewFinderClass); const CustomTreeViewFinder = styled(TreeViewFinder)(TreeViewFinderCustomStylesEmotion); @@ -215,7 +218,7 @@ function SnackErrorButton() {