Rework atom naming (#18240)

## Summary

- **Enhanced the `matching-state-variable` ESLint rule** to enforce
consistent naming for `ComponentState`, `FamilyState`, and
`ComponentFamilyState` hooks — previously it only covered `useAtomState`
and `useAtomStateValue`
- **The rule now checks 12 hooks** across three categories: value hooks
(`useAtomComponentStateValue`, `useAtomFamilyStateValue`, etc.), state
hooks (`useAtomComponentState`, `useAtomComponentFamilyState`), and
setter hooks (`useSetAtomState`, `useSetAtomComponentState`,
`useSetAtomFamilyState`, `useSetAtomComponentFamilyState`)
- **Fixed all 225 resulting lint violations** across 151 files, renaming
variables to match their state atom names (e.g. `currentViewId` →
`contextStoreCurrentViewId`, `selectedRecord` → `recordStore`). Cases
where the same state is accessed with different family keys/instance IDs
are suppressed with `eslint-disable-next-line`.

## Naming convention

| Hook | State argument | Valid | Invalid |
|------|---------------|-------|---------|
| `useAtomStateValue` | `fooState` | `const foo = ...` | `const bar =
...` |
| `useAtomComponentStateValue` | `fooComponentState` | `const foo = ...`
| `const bar = ...` |
| `useAtomFamilyStateValue` | `fooFamilyState` | `const foo = ...` |
`const bar = ...` |
| `useAtomComponentFamilyStateValue` | `fooComponentFamilyState` |
`const foo = ...` | `const bar = ...` |
| `useAtomState` | `fooState` | `const [foo, setFoo] = ...` | `const
[bar, setBar] = ...` |
| `useAtomComponentState` | `fooComponentState` | `const [foo, setFoo] =
...` | `const [bar, setBar] = ...` |
| `useAtomComponentFamilyState` | `fooComponentFamilyState` | `const
[foo, setFoo] = ...` | `const [bar, setBar] = ...` |
| `useSetAtomState` | `fooState` | `const setFoo = ...` | `const setBar
= ...` |
| `useSetAtomComponentState` | `fooComponentState` | `const setFoo =
...` | `const setBar = ...` |
| `useSetAtomFamilyState` | `fooFamilyState` | `const setFoo = ...` |
`const setBar = ...` |
| `useSetAtomComponentFamilyState` | `fooComponentFamilyState` | `const
setFoo = ...` | `const setBar = ...` |
This commit is contained in:
Charles Bochet
2026-02-25 22:28:25 +01:00
committed by GitHub
parent 1b805a36f3
commit bc4ae35bc4
321 changed files with 1824 additions and 1518 deletions
@@ -103,7 +103,7 @@ const IconPickerIcon = ({
Icon,
focusedIconKey,
}: IconPickerIconProps) => {
const isSelectedItemId = useAtomComponentStateValue(
const selectedItemId = useAtomComponentStateValue(
selectedItemIdComponentState,
iconKey,
);
@@ -116,7 +116,7 @@ const IconPickerIcon = ({
aria-label={convertIconKeyToLabel(iconKey)}
size="medium"
title={iconKey}
isSelected={iconKey === selectedIconKey || !!isSelectedItemId}
isSelected={iconKey === selectedIconKey || !!selectedItemId}
isFocused={iconKey === focusedIconKey}
Icon={Icon}
onClick={onSelect}
@@ -44,12 +44,13 @@ describe('useCloseDropdown', () => {
it('should close dropdown from inside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
);
const isInsideDropdownOpen = useAtomComponentStateValue(
const isDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
);
@@ -58,7 +59,7 @@ describe('useCloseDropdown', () => {
return {
isOutsideDropdownOpen,
isInsideDropdownOpen,
isDropdownOpen,
closeDropdown,
openDropdown,
};
@@ -72,26 +73,27 @@ describe('useCloseDropdown', () => {
result.current.openDropdown();
});
expect(result.current.isInsideDropdownOpen).toBe(true);
expect(result.current.isDropdownOpen).toBe(true);
expect(result.current.isOutsideDropdownOpen).toBe(false);
act(() => {
result.current.closeDropdown();
});
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(false);
});
it('should close dropdown from outside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
);
const isInsideDropdownOpen = useAtomComponentStateValue(
const isDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
);
@@ -100,7 +102,7 @@ describe('useCloseDropdown', () => {
return {
isOutsideDropdownOpen,
isInsideDropdownOpen,
isDropdownOpen,
closeDropdown,
openDropdown,
};
@@ -116,14 +118,14 @@ describe('useCloseDropdown', () => {
});
});
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(true);
act(() => {
result.current.closeDropdown(outsideDropdownId);
});
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(false);
});
});
@@ -42,57 +42,59 @@ describe('useOpenDropdown', () => {
it('should open dropdown from inside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
);
const isInsideDropdownOpen = useAtomComponentStateValue(
const isDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
);
const { openDropdown } = useOpenDropdown();
return { isOutsideDropdownOpen, isInsideDropdownOpen, openDropdown };
return { isOutsideDropdownOpen, isDropdownOpen, openDropdown };
},
{
wrapper: Wrapper,
},
);
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(false);
act(() => {
result.current.openDropdown();
});
expect(result.current.isInsideDropdownOpen).toBe(true);
expect(result.current.isDropdownOpen).toBe(true);
expect(result.current.isOutsideDropdownOpen).toBe(false);
});
it('should open dropdown from outside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
);
const isInsideDropdownOpen = useAtomComponentStateValue(
const isDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
);
const { openDropdown } = useOpenDropdown();
return { isInsideDropdownOpen, isOutsideDropdownOpen, openDropdown };
return { isDropdownOpen, isOutsideDropdownOpen, openDropdown };
},
{
wrapper: Wrapper,
},
);
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(false);
act(() => {
@@ -101,7 +103,7 @@ describe('useOpenDropdown', () => {
});
});
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(true);
});
});
@@ -43,69 +43,71 @@ describe('useToggleDropdown', () => {
it('should toggle dropdown from inside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
);
const isInsideDropdownOpen = useAtomComponentStateValue(
const isDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
);
const { toggleDropdown } = useToggleDropdown();
return { isOutsideDropdownOpen, isInsideDropdownOpen, toggleDropdown };
return { isOutsideDropdownOpen, isDropdownOpen, toggleDropdown };
},
{
wrapper: Wrapper,
},
);
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(false);
act(() => {
result.current.toggleDropdown();
});
expect(result.current.isInsideDropdownOpen).toBe(true);
expect(result.current.isDropdownOpen).toBe(true);
expect(result.current.isOutsideDropdownOpen).toBe(false);
act(() => {
result.current.toggleDropdown();
});
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(false);
act(() => {
result.current.toggleDropdown();
});
expect(result.current.isInsideDropdownOpen).toBe(true);
expect(result.current.isDropdownOpen).toBe(true);
expect(result.current.isOutsideDropdownOpen).toBe(false);
});
it('should toggle dropdown from outside component instance context', async () => {
const { result } = renderHook(
() => {
// eslint-disable-next-line twenty/matching-state-variable
const isOutsideDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
outsideDropdownId,
);
const isInsideDropdownOpen = useAtomComponentStateValue(
const isDropdownOpen = useAtomComponentStateValue(
isDropdownOpenComponentState,
);
const { toggleDropdown } = useToggleDropdown();
return { isOutsideDropdownOpen, isInsideDropdownOpen, toggleDropdown };
return { isOutsideDropdownOpen, isDropdownOpen, toggleDropdown };
},
{
wrapper: Wrapper,
},
);
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(false);
act(() => {
@@ -114,7 +116,7 @@ describe('useToggleDropdown', () => {
});
});
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(true);
act(() => {
@@ -123,7 +125,7 @@ describe('useToggleDropdown', () => {
});
});
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(false);
act(() => {
@@ -132,7 +134,7 @@ describe('useToggleDropdown', () => {
});
});
expect(result.current.isInsideDropdownOpen).toBe(false);
expect(result.current.isDropdownOpen).toBe(false);
expect(result.current.isOutsideDropdownOpen).toBe(true);
});
});
@@ -75,7 +75,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
const [signUpInNewWorkspaceMutation] = useSignUpInNewWorkspaceMutation();
const setMultiWorkspaceDropdownState = useSetAtomState(
const setMultiWorkspaceDropdown = useSetAtomState(
multiWorkspaceDropdownState,
);
@@ -195,9 +195,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
<MenuItem
LeftIcon={IconSwitchHorizontal}
text={t`Other workspaces`}
onClick={() =>
setMultiWorkspaceDropdownState('workspaces-list')
}
onClick={() => setMultiWorkspaceDropdown('workspaces-list')}
hasSubMenu={true}
/>
)}
@@ -215,7 +213,7 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
</>
}
hasSubMenu={true}
onClick={() => setMultiWorkspaceDropdownState('themes')}
onClick={() => setMultiWorkspaceDropdown('themes')}
/>
<UndecoratedLink
to={getSettingsPath(SettingsPath.WorkspaceMembersPage)}
@@ -14,7 +14,7 @@ export const MultiWorkspaceDropdownThemesComponents = () => {
const { setColorScheme, colorScheme, colorSchemeList } = useColorScheme();
const setMultiWorkspaceDropdownState = useSetAtomState(
const setMultiWorkspaceDropdown = useSetAtomState(
multiWorkspaceDropdownState,
);
@@ -23,7 +23,7 @@ export const MultiWorkspaceDropdownThemesComponents = () => {
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
onClick={() => setMultiWorkspaceDropdownState('default')}
onClick={() => setMultiWorkspaceDropdown('default')}
Icon={IconChevronLeft}
/>
}
@@ -19,7 +19,7 @@ export const MultiWorkspaceDropdownWorkspacesListComponents = () => {
const availableWorkspaces = useAtomStateValue(availableWorkspacesState);
const setMultiWorkspaceDropdownState = useSetAtomState(
const setMultiWorkspaceDropdown = useSetAtomState(
multiWorkspaceDropdownState,
);
const [searchValue, setSearchValue] = useState('');
@@ -29,7 +29,7 @@ export const MultiWorkspaceDropdownWorkspacesListComponents = () => {
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent
onClick={() => setMultiWorkspaceDropdownState('default')}
onClick={() => setMultiWorkspaceDropdown('default')}
Icon={IconChevronLeft}
/>
}
@@ -35,26 +35,26 @@ export const ScrollWrapper = ({
defaultEnableXScroll = true,
defaultEnableYScroll = true,
}: ScrollWrapperProps) => {
const setScrollTop = useSetAtomComponentState(
const setScrollWrapperScrollTop = useSetAtomComponentState(
scrollWrapperScrollTopComponentState,
componentInstanceId,
);
const setScrollLeft = useSetAtomComponentState(
const setScrollWrapperScrollLeft = useSetAtomComponentState(
scrollWrapperScrollLeftComponentState,
componentInstanceId,
);
const setScrollBottom = useSetAtomComponentState(
const setScrollWrapperScrollBottom = useSetAtomComponentState(
scrollWrapperScrollBottomComponentState,
componentInstanceId,
);
const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {
const target = event.currentTarget;
setScrollTop(target.scrollTop);
setScrollLeft(target.scrollLeft);
setScrollBottom(
setScrollWrapperScrollTop(target.scrollTop);
setScrollWrapperScrollLeft(target.scrollLeft);
setScrollWrapperScrollBottom(
target.scrollHeight - target.clientHeight - target.scrollTop,
);
};
@@ -10,7 +10,7 @@ export const useScrollRestoration = (componentInstanceId: string) => {
const storageKey = `scroll-${location.pathname}`;
const [isRestoring, setIsRestoring] = useState(false);
const scrollTop = useAtomComponentStateValue(
const scrollWrapperScrollTop = useAtomComponentStateValue(
scrollWrapperScrollTopComponentState,
componentInstanceId,
);
@@ -46,13 +46,13 @@ export const useScrollRestoration = (componentInstanceId: string) => {
useEffect(() => {
if (isRestoring) return;
if (scrollTop <= SCROLL_RESTORATION_TOP_THRESHOLD_PX) {
if (scrollWrapperScrollTop <= SCROLL_RESTORATION_TOP_THRESHOLD_PX) {
sessionStorage.removeItem(storageKey);
return;
}
sessionStorage.setItem(storageKey, scrollTop.toString());
}, [scrollTop, storageKey, isRestoring]);
sessionStorage.setItem(storageKey, scrollWrapperScrollTop.toString());
}, [scrollWrapperScrollTop, storageKey, isRestoring]);
useEffect(() => {
const savedPosition = sessionStorage.getItem(storageKey);