Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions admin-ui/.markdownlint-cli2.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
'dist',
'coverage',
'jans_config_api_orval',
'jans_config_api',
'.husky/_',
'.claude',
'CHANGELOG.md',
Expand Down
25 changes: 12 additions & 13 deletions admin-ui/app/components/Accordion/AccordionIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { ReactNode } from 'react'
import React, { type ReactElement } from 'react'
import clsx from 'clsx'
import { Add, Remove } from '@/components/icons'

import { Consumer } from './context'

interface AccordionIndicatorProps {
open?: ReactNode
closed?: ReactNode
type IndicatorElement = ReactElement<{ className?: string }>

type AccordionIndicatorProps = {
open?: IndicatorElement
closed?: IndicatorElement
className?: string
}

Expand All @@ -16,14 +18,11 @@ export const AccordionIndicator: React.FC<AccordionIndicatorProps> = ({
className,
}) => (
<Consumer>
{({ isOpen }) =>
isOpen
? React.cloneElement(open as React.ReactElement, {
className: clsx(className, (open as React.ReactElement).props.className),
})
: React.cloneElement(closed as React.ReactElement, {
className: clsx(className, (closed as React.ReactElement).props.className),
})
}
{({ isOpen }) => {
const indicator = isOpen ? open : closed
return React.cloneElement(indicator, {
className: clsx(className, indicator.props.className),
})
}}
</Consumer>
)
12 changes: 11 additions & 1 deletion admin-ui/app/components/App/AppMain.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { BrowserRouter as Router } from 'react-router-dom'
import { ErrorBoundary } from 'react-error-boundary'
import AuthenticatedRouteSelector from './AuthenticatedRouteSelector'
import GluuErrorScreen from 'Routes/Apps/Gluu/GluuErrorScreen'
import logUiCrash from '@/utils/logUiCrash'

const basePath = process.env.BASE_PATH ?? '/admin'

const AppMain = () => {
return (
<Router basename={basePath}>
<AuthenticatedRouteSelector />
<ErrorBoundary
FallbackComponent={GluuErrorScreen}
onError={(error, info) =>
logUiCrash(error instanceof Error ? error : new Error(String(error)), info.componentStack)
}
>
<AuthenticatedRouteSelector />
</ErrorBoundary>
</Router>
)
}
Expand Down
7 changes: 2 additions & 5 deletions admin-ui/app/components/BootstrapWrappers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,7 @@ export const Alert: React.FC<AlertProps> = ({
)
}

export const Input = React.forwardRef<
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,
InputProps
>(({ type = 'text', className, children, ...props }, ref) => {
export const Input = ({ ref, type = 'text', className, children, ...props }: InputProps) => {
if (type === 'select') {
return (
<select
Expand Down Expand Up @@ -170,7 +167,7 @@ export const Input = React.forwardRef<
{...(props as React.InputHTMLAttributes<HTMLInputElement>)}
/>
)
})
}
Input.displayName = 'Input'

const bootstrapColorToMui = (
Expand Down
3 changes: 3 additions & 0 deletions admin-ui/app/components/BootstrapWrappers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,19 @@ export type AlertProps = React.HTMLAttributes<HTMLDivElement> & {
type StandardInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'children'> & {
type?: Exclude<React.HTMLInputTypeAttribute, 'select' | 'textarea'>
children?: React.ReactNode
ref?: React.Ref<HTMLInputElement>
}

type SelectInputProps = React.SelectHTMLAttributes<HTMLSelectElement> & {
type: 'select'
children?: React.ReactNode
ref?: React.Ref<HTMLSelectElement>
}

type TextareaInputProps = React.TextareaHTMLAttributes<HTMLTextAreaElement> & {
type: 'textarea'
children?: React.ReactNode
ref?: React.Ref<HTMLTextAreaElement>
}

export type InputProps = StandardInputProps | SelectInputProps | TextareaInputProps
Expand Down
6 changes: 3 additions & 3 deletions admin-ui/app/components/GluuDropdown/GluuDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect, useContext, useMemo, useCallback, useId } from 'react'
import React, { useState, useRef, useEffect, use, useMemo, useCallback, useId } from 'react'
import Box from '@mui/material/Box'
import { ThemeContext } from 'Context/theme/themeContext'
import { THEME_DARK, DEFAULT_THEME } from '@/context/theme/constants'
Expand Down Expand Up @@ -44,7 +44,7 @@ export const GluuDropdown = <T extends DropdownValue = DropdownValue>({
const searchInputRef = useRef<HTMLInputElement>(null)
const listboxId = useId()

const theme = useContext(ThemeContext)
const theme = use(ThemeContext)
const currentTheme = theme?.state.theme || DEFAULT_THEME
const isDark = currentTheme === THEME_DARK
const dropdownBg = useMemo(() => {
Expand All @@ -68,7 +68,7 @@ export const GluuDropdown = <T extends DropdownValue = DropdownValue>({
const extractTextFromReactNode = useCallback((node: React.ReactNode): string => {
if (typeof node === 'string') return node
if (typeof node === 'number') return String(node)
if (React.isValidElement(node) && node.props.children) {
if (React.isValidElement<{ children?: React.ReactNode }>(node) && node.props.children) {
const { children } = node.props
if (Array.isArray(children)) {
return children.map((child) => extractTextFromReactNode(child)).join('')
Expand Down
4 changes: 2 additions & 2 deletions admin-ui/app/components/GluuModalShell/GluuModalShell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useContext, useMemo } from 'react'
import React, { useCallback, use, useMemo } from 'react'
import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
import { Close } from '@/components/icons'
Expand All @@ -15,7 +15,7 @@ const GluuModalShell = ({
children,
}: GluuModalShellProps) => {
const { t } = useTranslation()
const theme = useContext(ThemeContext)
const theme = use(ThemeContext)
const selectedTheme = theme?.state?.theme ?? DEFAULT_THEME
const isDark = selectedTheme === THEME_DARK
const themeColors = useMemo(() => getThemeColor(selectedTheme), [selectedTheme])
Expand Down
4 changes: 2 additions & 2 deletions admin-ui/app/components/GluuPageContent/GluuPageContent.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo, useContext, useMemo } from 'react'
import React, { memo, use, useMemo } from 'react'
import { ThemeContext } from '@/context/theme/themeContext'
import getThemeColor from '@/context/theme/config'
import { DEFAULT_THEME } from '@/context/theme/constants'
Expand All @@ -8,7 +8,7 @@ import type { GluuPageContentProps } from './types'

const GluuPageContent: React.FC<GluuPageContentProps> = memo(
({ children, className, withVerticalPadding = true, maxWidth, backgroundColor }) => {
const themeContext = useContext(ThemeContext)
const themeContext = use(ThemeContext)
const currentTheme = useMemo(
() => themeContext?.state.theme || DEFAULT_THEME,
[themeContext?.state.theme],
Expand Down
16 changes: 9 additions & 7 deletions admin-ui/app/components/GluuTable/GluuTable.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,21 +146,22 @@ export const useStyles = makeStyles<GluuTableStyleParams>()((
headerCellActions: {
textAlign: 'center',
},
headerCellSortable: {
padding: 0,
},
sortableHeader: {
'cursor': 'pointer',
'display': 'flex',
'alignItems': 'center',
'justifyContent': 'flex-start',
'position': 'absolute',
'left': 0,
'top': 0,
'right': 6,
'bottom': 0,
'width': 'auto',
'width': '100%',
'padding': '14px 16px',
'paddingRight': 20,
'boxSizing': 'border-box',
'background': 'none',
'border': 'none',
'font': 'inherit',
'lineHeight': '28px',
'color': headerColor,
'textAlign': 'inherit',
'&:hover [data-sort-icon]': {
Expand All @@ -178,7 +179,8 @@ export const useStyles = makeStyles<GluuTableStyleParams>()((
display: 'inline-flex',
alignItems: 'center',
opacity: OPACITY.NONE,
transition: 'opacity 0.15s ease',
transformOrigin: 'center',
transition: 'opacity 0.15s ease, transform 0.2s ease',
},
cell: {
padding: '14px 16px',
Expand Down
69 changes: 38 additions & 31 deletions admin-ui/app/components/GluuTable/GluuTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { ExpandMore as ExpandMoreIcon } from '@/components/icons'
import { useTheme } from '@/context/theme/themeContext'
import getThemeColor from '@/context/theme/config'
import { THEME_DARK } from '@/context/theme/constants'
import { ICON_SIZE } from '@/constants'
import GluuText from '@/routes/Apps/Gluu/GluuText'
import { GluuButton } from '@/components/GluuButton'
import { GluuSpinner } from '@/components/GluuSpinner'
import { useStyles, TABLE_MIN_WIDTH, TABLE_RESPONSIVE_BREAKPOINT } from './GluuTable.style'
import { T_KEYS, EMPTY_CELL_PLACEHOLDER } from './constants'
import type { CellValue, ColumnKey, GluuTableProps, SortDirection } from './types'
import { ChevronIcon } from '@/components/SVG'
import { ICON_SIZE } from '@/constants'
import {
getDefaultPagingSize,
getRowsPerPageOptions,
Expand Down Expand Up @@ -401,13 +401,9 @@ const GluuTable = <T,>(props: Readonly<GluuTableProps<T>>) => {
const handleSort = useCallback((columnKey: string) => {
setSortState((prev) => {
const isSameColumn = prev.column === columnKey
const nextDirection: SortDirection = !isSameColumn
? 'asc'
: prev.direction === 'asc'
? 'desc'
: null
const nextDirection: SortDirection = isSameColumn && prev.direction === 'asc' ? 'desc' : 'asc'
return {
column: nextDirection ? columnKey : null,
column: columnKey,
direction: nextDirection,
}
})
Expand Down Expand Up @@ -510,7 +506,11 @@ const GluuTable = <T,>(props: Readonly<GluuTableProps<T>>) => {
<th
ref={(el) => setHeaderCellRef(id, el)}
key={`${id}-${colIdx}`}
className={`${classes.headerCell} ${classes.headerCellResizable}`}
className={
isSortable
? `${classes.headerCell} ${classes.headerCellSortable}`
: `${classes.headerCell} ${classes.headerCellResizable}`
}
style={{
width: effectiveWidths[id],
...(parseMinWidth(col) != null && { minWidth: parseMinWidth(col) }),
Expand All @@ -529,36 +529,43 @@ const GluuTable = <T,>(props: Readonly<GluuTableProps<T>>) => {
onClick={() => handleSort(id)}
>
{col.label}
<span className={classes.sortIconWrap} data-sort-icon>
<ChevronIcon
width={14}
height={14}
direction={isActive && sortState.direction === 'asc' ? 'up' : 'down'}
/>
<span
className={classes.sortIconWrap}
data-sort-icon
style={{
transform:
isActive && sortState.direction === 'asc'
? 'rotate(180deg)'
: 'rotate(0deg)',
}}
>
<ChevronIcon width={14} height={14} />
</span>
</button>
) : (
<GluuText variant="span" disableThemeColor>
{col.label}
</GluuText>
)}
<div
role="separator"
aria-orientation="vertical"
aria-label="Resize column"
className={classes.resizeHandle}
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
handleResizeStart(id, e.clientX, e.currentTarget)
}}
onTouchStart={(e) => {
e.preventDefault()
if (e.touches.length > 0) {
handleResizeStart(id, e.touches[0].clientX, e.currentTarget)
}
}}
/>
{(colIdx < columns.length - 1 || (actions?.length ?? 0) > 0) && (
<div
role="separator"
aria-orientation="vertical"
aria-label="Resize column"
className={classes.resizeHandle}
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
handleResizeStart(id, e.clientX, e.currentTarget)
}}
onTouchStart={(e) => {
e.preventDefault()
if (e.touches.length > 0) {
handleResizeStart(id, e.touches[0].clientX, e.currentTarget)
}
}}
/>
)}
</th>
)
})}
Expand Down
Loading