Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e90dbd9
feat(admin-ui): revamp Audit Logs and reusable Table component as per…
faisalsiddique4400 Feb 11, 2026
66f276e
feat(admin-ui): revamp Audit Logs and reusable Table component as per…
faisalsiddique4400 Feb 11, 2026
690ae74
feat(admin-ui): revamp Audit Logs and reusable Table component as per…
faisalsiddique4400 Feb 11, 2026
77679c7
feat(admin-ui): revamp Audit Logs and reusable Table component as per…
faisalsiddique4400 Feb 11, 2026
65fc043
new design changes
faisalsiddique4400 Feb 12, 2026
79b490a
border bottom implemented
faisalsiddique4400 Feb 13, 2026
b5dd4de
Merge branch 'main' into admin-ui-issue-2646
faisalsiddique4400 Feb 13, 2026
23758ed
Code rabbit implementations
faisalsiddique4400 Feb 13, 2026
3756ce5
Code rabbit implementations
faisalsiddique4400 Feb 13, 2026
2c6b5a1
Code rabbit implementations
faisalsiddique4400 Feb 13, 2026
c4789c7
initial webhook
faisalsiddique4400 Feb 12, 2026
b344e8b
feat(admin-ui): revamp Home -> WebHooks page as per Figma (#2629)
faisalsiddique4400 Feb 17, 2026
a59cf27
feat(admin-ui): revamp Home -> WebHooks page as per Figma (#2629)
faisalsiddique4400 Feb 18, 2026
be68a84
Merge remote-tracking branch 'origin/main' into amdin-ui-issue-2629
faisalsiddique4400 Feb 19, 2026
2d8ff15
minor changes
faisalsiddique4400 Feb 19, 2026
6472183
theme tooltip centralized
faisalsiddique4400 Feb 19, 2026
c6f19a1
coderabbit suggestions
faisalsiddique4400 Feb 20, 2026
b851490
coderabbit suggestions
faisalsiddique4400 Feb 20, 2026
e823740
coderabbit suggestions
faisalsiddique4400 Feb 22, 2026
effbcbf
Merge branch 'main' into amdin-ui-issue-2629
faisalsiddique4400 Feb 22, 2026
6584a16
coderabbit suggestions
faisalsiddique4400 Feb 22, 2026
d218eca
coderabbit suggestions
faisalsiddique4400 Feb 23, 2026
c8c5cd4
coderabbit suggestions
faisalsiddique4400 Feb 23, 2026
3617e5c
coderabbit suggestions
faisalsiddique4400 Feb 23, 2026
18a6c6d
initial theme changes in customs script
faisalsiddique4400 Feb 23, 2026
e3aeaac
form titles issues fix
faisalsiddique4400 Feb 23, 2026
88f6b40
form titles issues fix
faisalsiddique4400 Feb 23, 2026
e635bb3
custom script table
faisalsiddique4400 Feb 24, 2026
118549c
merge with main
faisalsiddique4400 Feb 24, 2026
faaf9b9
table theme settings for row dynamic width
faisalsiddique4400 Feb 25, 2026
3da01a2
fix: rename applicationstyle.tsx to applicationStyle.tsx for consiste…
faisalsiddique4400 Feb 28, 2026
b68889c
Merge origin/main into admin-ui-issue-2631
faisalsiddique4400 Feb 28, 2026
e70a944
Merge with main
faisalsiddique4400 Mar 2, 2026
6a5363b
Merge with main
faisalsiddique4400 Mar 2, 2026
d896391
script search toolbar fixes
faisalsiddique4400 Mar 2, 2026
9f680ef
script table, and form integrated
faisalsiddique4400 Mar 2, 2026
4dbdb74
error modal designing and redux cleanup
faisalsiddique4400 Mar 3, 2026
7ffb66e
further redux cleanup
faisalsiddique4400 Mar 3, 2026
9b01976
test files sync with UI
faisalsiddique4400 Mar 3, 2026
d65a24d
Merge branch 'main' into admin-ui-2631
faisalsiddique4400 Mar 3, 2026
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
5 changes: 1 addition & 4 deletions admin-ui/__tests__/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jest.spyOn(global.console, 'error').mockImplementation((...args: unknown[]) => {
msg.includes('Problems getting API access token') ||
msg.includes('Problems posting user action audit log') ||
msg.includes('suspended resource finished loading') ||
msg.includes('not wrapped in act(') ||
(msg.includes('Failed prop type') && msg.includes('timeout') && msg.includes('Fade'))
Comment on lines +32 to 33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n admin-ui/__tests__/setup.ts

Repository: GluuFederation/flex

Length of output: 2065


🏁 Script executed:

# Search for act() related issues or comments in the codebase
rg "act\(" admin-ui/__tests__ -B 2 -A 2 | head -50

# Check for any TODO or FIXME comments related to this
rg "act|async.*test|wrapped in act" admin-ui -i | grep -i "todo\|fixme\|hack\|workaround" | head -20

# Look for other setup files or similar patterns
find admin-ui -name "*.ts" -path "*__tests__*" -o -name "setup*" | head -20

Repository: GluuFederation/flex

Length of output: 1720


🏁 Script executed:

# Check if there's a setup-tests.ts file that might have related context
cat -n admin-ui/__tests__/setup-tests.ts 2>/dev/null | head -50

# Search for any issues or discussions about act() warnings in comments
rg "act\(" -B 5 -A 5 admin-ui --type ts --type js | grep -i "issue\|problem\|warning\|suppress" | head -20

# Check git log for when this suppression was added
git log -p --all -S "not wrapped in act(" -- admin-ui/__tests__/setup.ts | head -100

Repository: GluuFederation/flex

Length of output: 506


🏁 Script executed:

# Get more details from git log about when act() suppression was added
git log --all --oneline -- admin-ui/__tests__/setup.ts | head -20

# Check if there are any test failures or skipped tests related to act()
rg "skip\|xdescribe\|xit\|\.skip" admin-ui/__tests__ -B 2 -A 2 | head -40

# Search for any act() usage in actual tests to see if warnings are being actively triggered
rg "act\(" admin-ui --type ts --type js | grep -v "setup.ts\|node_modules" | head -20

# Check if there are any comments or documentation about this setup
rg "console.error\|suppress\|skip.*warning" admin-ui/__tests__ -B 2 -A 2

Repository: GluuFederation/flex

Length of output: 368


🏁 Script executed:

# Search for test patterns using waitFor, act, or async operations
rg "waitFor|userEvent|fireEvent" admin-ui/__tests__ --type ts --type js | wc -l

# Check if tests are actually structured to handle async properly
rg "async\s*\(" admin-ui/__tests__ --type ts --type js | head -20

# Look for any comments in test files about act() or async issues
rg "act\|async\|Warning" admin-ui/__tests__ --type ts --type js -B 1 -A 1 | grep -E "//|/\*|\*/" | head -30

# Check if there are any recent issues or PRs discussing this
git log --all --grep="act\|async\|test" --oneline | head -20

Repository: GluuFederation/flex

Length of output: 1767


🏁 Script executed:

# Look at actual test files to see async patterns and if they properly use act()
find admin-ui/__tests__ -name "*.test.ts" -o -name "*.test.js" | head -5 | xargs -I {} bash -c 'echo "=== {} ===" && head -50 {}'

# Check if tests are using React Testing Library properly
rg "render\|screen\|within" admin-ui/__tests__ --type ts --type js | wc -l

# Look for any potential unhandled async issues in tests
rg "setState\|useEffect\|useState" admin-ui/__tests__ --type ts --type js | head -10

Repository: GluuFederation/flex

Length of output: 64


🏁 Script executed:

# List test files to understand the testing structure
find admin-ui -name "*.test.ts" -o -name "*.test.js" -o -name "*.test.tsx" | head -10

# Check a specific test file to see the pattern
find admin-ui -name "*.test.ts" -type f | head -1 | xargs cat | head -60

# Search for React Query or async test patterns
rg "useQuery|useMutation|waitFor" admin-ui --type ts --type js | head -15

Repository: GluuFederation/flex

Length of output: 4274


🏁 Script executed:

# Look at a component test to see async patterns
cat -n admin-ui/app/routes/Apps/Gluu/Tests/GluuTooltip.test.tsx

# Search for act() calls or warnings about act() in test output comments
rg "act\(" admin-ui --type ts --type js -B 3 -A 3 | head -40

# Check the React version to understand the context
rg "react.*version\|\"react\":" admin-ui/package.json

Repository: GluuFederation/flex

Length of output: 2564


🏁 Script executed:

# Check if there's any evidence of why this suppression was needed
# Look for any test-related issues or discussions
git log --all -p -- admin-ui/__tests__/setup.ts | grep -A 20 -B 20 "act(" | head -100

# Search for any test failures or issues with act() warnings in recent commits
git log --all --oneline --grep="test\|act\|warning" | head -30

# Check if tests are properly wrapped with act() or waitFor
rg "waitFor\|userEvent\|fireEvent" admin-ui/__tests__ admin-ui/app --type ts --type tsx | wc -l

Repository: GluuFederation/flex

Length of output: 3860


🏁 Script executed:

# Check if there are any test files that specifically deal with async operations
rg "userEvent\|fireEvent\|waitFor" admin-ui --type tsx --type ts | wc -l

# Look for any evidence that act() warnings were being triggered before suppression
# Check if there are any comments or TODOs about this
rg "act\(\)|Warning.*act|act.*warning" admin-ui -i -B 2 -A 2

# Examine the specific commit that added this suppression
git log --all --format="%H %s" -- admin-ui/__tests__/setup.ts | head -5 | while read hash subject; do
  echo "=== $subject ==="
  git show --stat $hash | head -20
done

Repository: GluuFederation/flex

Length of output: 2993


🏁 Script executed:

# Check if there are any test configuration or jest setup improvements in recent history
git log --all --oneline -20 | grep -i "test\|jest\|setup"

# See if there's a PR or branch info that might explain the act() suppression
git log --all --format="%H %s %b" -- admin-ui/__tests__/setup.ts | grep -i "act\|async\|warning" -A 5 -B 5 | head -50

# Check if the test suite has any skipped or xfailing tests related to act
rg "skip\|xdescribe\|xit" admin-ui/__tests__ -l | xargs -I {} bash -c 'echo "=== {} ===" && grep -A 2 -B 2 "skip\|xdescribe\|xit" {}'

Repository: GluuFederation/flex

Length of output: 140


Remove the global act() warning suppression.

Suppressing React's "not wrapped in act()" warnings masks real async test defects. Tests should be fixed to properly handle state updates with waitFor(), userEvent, or other React Testing Library patterns rather than hiding the warning.

Suggested fix
-    msg.includes('not wrapped in act(') ||
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
msg.includes('not wrapped in act(') ||
(msg.includes('Failed prop type') && msg.includes('timeout') && msg.includes('Fade'))
(msg.includes('Failed prop type') && msg.includes('timeout') && msg.includes('Fade'))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin-ui/__tests__/setup.ts` around lines 32 - 33, The current global
suppression in admin-ui/__tests__/setup.ts filters out React's "not wrapped in
act()" warnings (the msg.includes('not wrapped in act(') branch) which hides
real async test issues; remove that suppression and update the tests producing
the warnings to properly await state updates using React Testing Library
utilities (e.g., waitFor, findBy*, or userEvent async flows) or wrap the
specific updates in act() where appropriate so state changes are awaited rather
than globally silencing the error.


if (shouldSuppress) return
Expand All @@ -51,7 +52,3 @@ if (typeof globalThis.URL !== 'undefined') {
}

global.ResizeObserver = ResizeObserverPolyfill

it('Jans-admin UI test setup', () => {
expect(true).toBeTruthy()
})
11 changes: 3 additions & 8 deletions admin-ui/app/components/Accordion/AccordionBody.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
// @ts-nocheck
import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { Collapse, CardBody } from 'reactstrap'

import { Consumer } from './context'
import type { AccordionBodyProps } from './Accordion.d'

export const AccordionBody = (props) => (
export const AccordionBody: React.FC<AccordionBodyProps> = ({ children, className }) => (
<Consumer>
{({ isOpen }) => (
<Collapse isOpen={isOpen}>
<CardBody className={classNames(props.className, 'pt-0')}>{props.children}</CardBody>
<CardBody className={classNames(className, 'pt-0')}>{children}</CardBody>
</Collapse>
)}
</Consumer>
)
AccordionBody.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
}
2 changes: 1 addition & 1 deletion admin-ui/app/components/Accordion/AccordionIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Consumer } from './context'
interface AccordionIndicatorProps {
open?: ReactNode
closed?: ReactNode
className?: any
className?: string
}

export const AccordionIndicator: React.FC<AccordionIndicatorProps> = ({
Expand Down
10 changes: 8 additions & 2 deletions admin-ui/app/components/Accordion/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
// @ts-nocheck
import { Accordion } from './Accordion'
import { Accordion as AccordionBase } from './Accordion'
import { AccordionBody } from './AccordionBody'
import { AccordionHeader } from './AccordionHeader'
import { AccordionIndicator } from './AccordionIndicator'

type AccordionComponent = typeof AccordionBase & {
Body: typeof AccordionBody
Header: typeof AccordionHeader
Indicator: typeof AccordionIndicator
}

const Accordion = AccordionBase as AccordionComponent
Comment on lines +6 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Prefer the shared AccordionComponent type to avoid type drift.

This file redefines a type that already exists in admin-ui/app/components/Accordion/Accordion.d.ts (Line 25-29). Reusing the shared type keeps the public API contract in one place.

♻️ Proposed refactor
 import { Accordion as AccordionBase } from './Accordion'
 import { AccordionBody } from './AccordionBody'
 import { AccordionHeader } from './AccordionHeader'
 import { AccordionIndicator } from './AccordionIndicator'
+import type { AccordionComponent } from './Accordion.d'
 
-type AccordionComponent = typeof AccordionBase & {
-  Body: typeof AccordionBody
-  Header: typeof AccordionHeader
-  Indicator: typeof AccordionIndicator
-}
-
 const Accordion = AccordionBase as AccordionComponent
 Accordion.Body = AccordionBody
 Accordion.Header = AccordionHeader
 Accordion.Indicator = AccordionIndicator
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type AccordionComponent = typeof AccordionBase & {
Body: typeof AccordionBody
Header: typeof AccordionHeader
Indicator: typeof AccordionIndicator
}
const Accordion = AccordionBase as AccordionComponent
import { Accordion as AccordionBase } from './Accordion'
import { AccordionBody } from './AccordionBody'
import { AccordionHeader } from './AccordionHeader'
import { AccordionIndicator } from './AccordionIndicator'
import type { AccordionComponent } from './Accordion.d'
const Accordion = AccordionBase as AccordionComponent
Accordion.Body = AccordionBody
Accordion.Header = AccordionHeader
Accordion.Indicator = AccordionIndicator
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin-ui/app/components/Accordion/index.ts` around lines 6 - 12, The local
redefinition of AccordionComponent should be removed and the shared
AccordionComponent type reused: import the existing AccordionComponent type and
then cast AccordionBase using that imported type (keep references to
AccordionBase, AccordionBody, AccordionHeader, AccordionIndicator), instead of
declaring a new type locally; delete the local type alias and replace "const
Accordion = AccordionBase as AccordionComponent" with a cast to the
imported/shared AccordionComponent to ensure the public API type is consistent.

Accordion.Body = AccordionBody
Accordion.Header = AccordionHeader
Accordion.Indicator = AccordionIndicator
Expand Down
1 change: 0 additions & 1 deletion admin-ui/app/components/App/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import AppMain from './AppMain'

export default AppMain
18 changes: 15 additions & 3 deletions admin-ui/app/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,26 @@ interface AvatarProps {
const Avatar: React.FC<AvatarProps> = (props) => {
const avatarClass = classNames('avatar', `avatar--${props.size}`, props.className)
const addOnsdArr = React.Children.toArray(props.addOns)
const badge = find(addOnsdArr, (avatarAddOn: any) => avatarAddOn.type.addOnId === 'avatar--badge')
type AddOnElement = React.ReactElement<{ addOnId?: string }> & {
type: { addOnId?: string }
props?: { small?: boolean }
}
const badge = find(
addOnsdArr,
(avatarAddOn: React.ReactNode) =>
React.isValidElement(avatarAddOn) &&
(avatarAddOn as AddOnElement).type?.addOnId === 'avatar--badge',
)
const icons = filter(
addOnsdArr,
(avatarAddOn: any) => avatarAddOn.type.addOnId === 'avatar--icon',
(avatarAddOn: React.ReactNode) =>
React.isValidElement(avatarAddOn) &&
(avatarAddOn as AddOnElement).type?.addOnId === 'avatar--icon',
)
const isNested = reduce(
addOnsdArr,
(acc: boolean, avatarAddOn: any) => acc || !!avatarAddOn.props.small,
(acc: boolean, avatarAddOn: React.ReactNode) =>
acc || (React.isValidElement(avatarAddOn) && !!(avatarAddOn as AddOnElement).props?.small),
false,
)

Expand Down
9 changes: 7 additions & 2 deletions admin-ui/app/components/Avatar/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// @ts-nocheck
import { Avatar } from './Avatar'
import { Avatar as AvatarBase } from './Avatar'
import { AvatarFont } from './AvatarFont'
import { AvatarImage } from './AvatarImage'

import { AvatarAddonBadge } from './AvatarAddonBadge'
import { AvatarAddonIcon } from './AvatarAddonIcon'

type AvatarComponent = typeof AvatarBase & {
Font: typeof AvatarFont
Image: typeof AvatarImage
}

const Avatar = AvatarBase as AvatarComponent
Avatar.Font = AvatarFont
Avatar.Image = AvatarImage

Expand Down
4 changes: 2 additions & 2 deletions admin-ui/app/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { Card as BsCard } from 'reactstrap'
import classes from './Card.scss'

interface CardProps extends Omit<React.ComponentProps<typeof BsCard>, 'color'> {
children: any
children: React.ReactNode
type?: string
color?: string | null
className?: any
className?: string
}

const Card = ({ children, type = 'border', color = null, className, ...otherProps }: CardProps) => {
Expand Down
1 change: 0 additions & 1 deletion admin-ui/app/components/Card/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { Card } from './Card'

export default Card
1 change: 0 additions & 1 deletion admin-ui/app/components/CardHeader/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { CardHeader } from './CardHeader'

export default CardHeader
20 changes: 14 additions & 6 deletions admin-ui/app/components/CustomInput/CustomInput.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
// @ts-nocheck
import React from 'react'
import classNames from 'classnames'
import { Input as RSCustomInput } from 'reactstrap'

const CustomInput = (props) => {
const { className, ...otherProps } = props
type CustomInputProps = React.ComponentProps<typeof RSCustomInput>

const CustomInput: React.FC<CustomInputProps> = (props) => {
const { className, label, name, ...otherProps } = props
const inputClass = classNames(className, {
'custom-control-empty': !props.label,
'custom-control-empty': !label,
})

return <RSCustomInput data-testid={props.name} className={inputClass} {...otherProps} />
return (
<RSCustomInput
data-testid={name}
className={inputClass}
label={label}
name={name}
{...otherProps}
/>
)
Comment on lines +13 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider handling undefined name for data-testid.

When name is undefined, the data-testid attribute will also be undefined, which could affect test selectors that depend on this attribute. Consider providing a fallback or omitting the attribute when name is not provided.

💡 Proposed fix
   return (
     <RSCustomInput
-      data-testid={name}
+      data-testid={name ?? undefined}
       className={inputClass}
       label={label}
       name={name}
       {...otherProps}
     />
   )

Or conditionally spread the attribute:

return (
  <RSCustomInput
    {...(name && { 'data-testid': name })}
    className={inputClass}
    label={label}
    name={name}
    {...otherProps}
  />
)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<RSCustomInput
data-testid={name}
className={inputClass}
label={label}
name={name}
{...otherProps}
/>
)
return (
<RSCustomInput
data-testid={name ?? undefined}
className={inputClass}
label={label}
name={name}
{...otherProps}
/>
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin-ui/app/components/CustomInput/CustomInput.tsx` around lines 13 - 21,
The data-testid prop on RSCustomInput can be undefined when name is undefined;
update CustomInput.tsx to avoid rendering data-testid when name is falsy by
conditionally including that attribute (e.g., only spread {'data-testid': name}
when name is truthy) so RSCustomInput receives data-testid only for valid names;
keep the rest of the props (className=inputClass, label, name, ...otherProps)
unchanged.

}
CustomInput.propTypes = { ...RSCustomInput.propTypes }

export { CustomInput }
1 change: 0 additions & 1 deletion admin-ui/app/components/CustomInput/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { CustomInput } from './CustomInput'

export default CustomInput
16 changes: 7 additions & 9 deletions admin-ui/app/components/Divider/Divider.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// @ts-nocheck
import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

export const Divider = ({ position, children, className }) => {
interface DividerProps {
position?: 'center' | 'right'
children?: React.ReactNode
className?: string
}

export const Divider: React.FC<DividerProps> = ({ position, children, className }) => {
const dividerClass = classNames(
{
'hr-text-center': position === 'center',
Expand All @@ -15,9 +19,3 @@ export const Divider = ({ position, children, className }) => {

return <div className={dividerClass}>{children}</div>
}

Divider.propTypes = {
position: PropTypes.string,
className: PropTypes.string,
children: PropTypes.node,
}
1 change: 0 additions & 1 deletion admin-ui/app/components/Divider/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-nocheck
import { Divider } from './Divider'

export default Divider
6 changes: 3 additions & 3 deletions admin-ui/app/components/GluuButton/GluuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fontFamily } from '@/styles/fonts'
import { useTheme } from '@/context/theme/themeContext'
import getThemeColor from '@/context/theme/config'
import { THEME_DARK } from '@/context/theme/constants'
import { OPACITY } from '@/constants'
import { OPACITY, getHoverOpacity } from '@/constants'
import { resolveBackgroundColor } from '@/utils/buttonUtils'
import type { GluuButtonProps } from './types'

Expand Down Expand Up @@ -56,7 +56,7 @@ const GluuButton: React.FC<GluuButtonProps> = (props) => {
const border = borderColor ?? (isDark ? 'transparent' : themeColors.borderColor)
const hoverBg = isDark ? themeColors.lightBackground : themeColors.borderColor
const keepBgOnHover = !disableHoverStyles && useOpacityOnHover && isHovered && !isDisabled
const opacityOnHover = hoverOpacity ?? OPACITY.DIMMED
const opacityOnHover = hoverOpacity ?? 1 - getHoverOpacity(isDark)

return {
display: 'inline-flex',
Expand All @@ -82,7 +82,7 @@ const GluuButton: React.FC<GluuButtonProps> = (props) => {
),
color: outlined ? (textColor ?? themeColors.fontColor) : text,
cursor: isDisabled ? 'not-allowed' : 'pointer',
opacity: isDisabled ? OPACITY.DIMMED : keepBgOnHover ? opacityOnHover : 1,
opacity: isDisabled ? OPACITY.DISABLED : keepBgOnHover ? opacityOnHover : 1,
width: block ? '100%' : 'auto',
transition: 'background-color 0.15s ease-in-out, opacity 0.15s ease-in-out',
...style,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useMemo } from 'react'
import { makeStyles } from 'tss-react/mui'
import type { SxProps, Theme } from '@mui/material/styles'
import type { ThemeConfig } from '@/context/theme/config'
import { hexToRgb } from '@/customColors'
import { OPACITY } from '@/constants/ui'
import { getLoadingOverlayRgba } from '@/customColors'
import { getHoverOpacity } from '@/constants'
import { fontFamily, fontSizes, fontWeights, letterSpacing } from '@/styles/fonts'

const TEXT_FIELD_SIZE = 'small' as const
Expand Down Expand Up @@ -39,7 +39,7 @@ const buildPickerThemeColors = (
): PickerThemeColors => {
const inputText = textColor || themeConfig.fontColor
const borderColor = isDark ? 'transparent' : themeConfig.borderColor
const hoverBg = `rgba(${hexToRgb(themeConfig.fontColor)}, ${isDark ? OPACITY.HOVER_DARK : OPACITY.HOVER_LIGHT})`
const hoverBg = getLoadingOverlayRgba(themeConfig.fontColor, getHoverOpacity(isDark))
return {
labelBackground: backgroundColor || themeConfig.background,
inputBackground: themeConfig.inputBackground,
Expand Down
20 changes: 20 additions & 0 deletions admin-ui/app/components/GluuDetailGrid/GluuDetailGrid.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { makeStyles } from 'tss-react/mui'
import { SPACING } from '@/constants'

/** Min width per cell for ~5 columns on desktop; prevents label/value overlap */
const MIN_CELL_WIDTH = 160

export const useStyles = makeStyles()(() => ({
detailGrid: {
display: 'grid',
gridTemplateColumns: `repeat(auto-fill, minmax(${MIN_CELL_WIDTH}px, 1fr))`,
gap: `${SPACING.SECTION_GAP}px ${SPACING.SECTION_GAP}px`,
width: '100%',
minWidth: 0,
},
detailItem: {
minWidth: 0,
overflowWrap: 'break-word',
wordBreak: 'break-word',
},
}))
46 changes: 46 additions & 0 deletions admin-ui/app/components/GluuDetailGrid/GluuDetailGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { memo } from 'react'
import GluuFormDetailRow from 'Routes/Apps/Gluu/GluuFormDetailRow'
import { useStyles } from './GluuDetailGrid.style'
import type { GluuDetailGridProps, GluuDetailGridField } from './types'

const getFieldKey = (field: GluuDetailGridField, idx: number): string =>
field.doc_entry ?? `${field.label}-${idx}`

const GluuDetailGrid: React.FC<GluuDetailGridProps> = ({
fields,
labelStyle,
defaultDocCategory,
className,
layout = 'column',
}) => {
const { classes } = useStyles()

return (
<div className={`${classes.detailGrid} ${className ?? ''}`.trim()}>
{fields.map((field, idx) => (
<div key={getFieldKey(field, idx)} className={classes.detailItem}>
<GluuFormDetailRow
label={field.label}
value={field.value}
doc_entry={field.doc_entry}
doc_category={field.doc_category ?? defaultDocCategory}
isBadge={field.isBadge}
badgeBackgroundColor={field.badgeBackgroundColor}
badgeTextColor={field.badgeTextColor}
isDirect={field.isDirect}
lsize={field.lsize}
rsize={field.rsize}
labelStyle={
labelStyle || field.labelStyle ? { ...labelStyle, ...field.labelStyle } : undefined
}
valueStyle={field.valueStyle}
rowClassName={field.rowClassName}
layout={field.layout ?? layout}
/>
</div>
))}
</div>
)
}

export default memo(GluuDetailGrid)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react'
import { render, screen } from '@testing-library/react'
import { GluuDetailGrid } from '@/components/GluuDetailGrid'
import AppTestWrapper from 'Routes/Apps/Gluu/Tests/Components/AppTestWrapper'

const Wrapper = ({ children }: { children: React.ReactNode }) => (
<AppTestWrapper>{children}</AppTestWrapper>
)

const mockFields = [
{ label: 'fields.inum', value: '09A0-93D7', doc_entry: 'inum' },
{ label: 'fields.name', value: 'smpp', doc_entry: 'name' },
{ label: 'fields.script_type', value: 'person_authentication', doc_entry: 'scriptType' },
]

it('renders all field labels and values', () => {
render(<GluuDetailGrid fields={mockFields} />, { wrapper: Wrapper })
expect(screen.getByText(/Inum/i)).toBeInTheDocument()
expect(screen.getByText(/Name/i)).toBeInTheDocument()
expect(screen.getByText(/Script Type/i)).toBeInTheDocument()
expect(screen.getByText('09A0-93D7')).toBeInTheDocument()
expect(screen.getByText('smpp')).toBeInTheDocument()
expect(screen.getByText('person_authentication')).toBeInTheDocument()
})

it('renders with defaultDocCategory', () => {
render(<GluuDetailGrid fields={mockFields} defaultDocCategory="SCRIPT" />, {
wrapper: Wrapper,
})
expect(screen.getByText('smpp')).toBeInTheDocument()
})

it('renders with labelStyle', () => {
render(<GluuDetailGrid fields={mockFields} labelStyle={{ color: 'red' }} />, { wrapper: Wrapper })
expect(screen.getByText(/Inum/i)).toBeInTheDocument()
})
Comment on lines +16 to +36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider wrapping tests in a describe block and expanding coverage.

The tests provide basic validation but could be improved:

  1. Wrap tests in a describe('GluuDetailGrid', () => { ... }) block for better organization and reporting.
  2. Consider adding tests for:
    • Empty fields array handling
    • The layout and className props
    • Edge cases with missing/undefined field values
♻️ Suggested structure
+describe('GluuDetailGrid', () => {
-it('renders all field labels and values', () => {
+  it('renders all field labels and values', () => {
   render(<GluuDetailGrid fields={mockFields} />, { wrapper: Wrapper })
   // ... assertions
-})
+  })
 
-it('renders with defaultDocCategory', () => {
+  it('renders with defaultDocCategory', () => {
   // ...
-})
+  })
 
-it('renders with labelStyle', () => {
+  it('renders with labelStyle', () => {
   // ...
+  })
+
+  it('handles empty fields array', () => {
+    render(<GluuDetailGrid fields={[]} />, { wrapper: Wrapper })
+    // Assert graceful empty state
+  })
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin-ui/app/components/GluuDetailGrid/__tests__/GluuDetailGrid.test.tsx`
around lines 16 - 36, Wrap the existing tests in a describe('GluuDetailGrid', ()
=> { ... }) block and add unit tests in the same file that cover: rendering when
fields=[] (expect no labels/values or a placeholder), passing different layout
values to the GluuDetailGrid component (verify DOM layout/class changes),
providing a className prop (assert it appears on the root element), and handling
missing/undefined field values in mockFields (assert no crashes and appropriate
fallback UI). Use the existing mockFields and Wrapper to build these tests and
reference the GluuDetailGrid component and its props (fields,
defaultDocCategory, labelStyle, layout, className) when locating where to add
the new test cases.

2 changes: 2 additions & 0 deletions admin-ui/app/components/GluuDetailGrid/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as GluuDetailGrid } from './GluuDetailGrid'
export type { GluuDetailGridProps, GluuDetailGridField } from './types'
26 changes: 26 additions & 0 deletions admin-ui/app/components/GluuDetailGrid/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { CSSProperties } from 'react'

export type GluuDetailGridField = {
label: string
value?: string | number | boolean | null
doc_entry?: string
doc_category?: string
isBadge?: boolean
badgeBackgroundColor?: string
badgeTextColor?: string
isDirect?: boolean
lsize?: number
rsize?: number
labelStyle?: CSSProperties
valueStyle?: CSSProperties
rowClassName?: string
layout?: 'row' | 'column'
}

export type GluuDetailGridProps = {
fields: GluuDetailGridField[]
labelStyle?: CSSProperties
defaultDocCategory?: string
className?: string
layout?: 'row' | 'column'
}
Loading