From 1466d44b57091a9a2c38d6b8fa27d2ae0924183e Mon Sep 17 00:00:00 2001
From: Vardhaman Bhandari <97441447+Vardhaman619@users.noreply.github.com>
Date: Mon, 21 Oct 2024 21:52:10 +0530
Subject: [PATCH] fix: Make the entire advanced mode toggle container clickable
(#7761)
In this PR:
- Use a real ` ` element in the ` `
component
- Create an `accessibility` module in the `twenty-ui` package
- Export the `VISIBILITY_HIDDEN` CSS object to hide visually any element
- Export a ` ` component from the `twenty-ui` package
to add visually hidden textual information easily
- Export a ` ` component to create custom form
control components easily
- Use a `` element for the "Advanced:" text; it will naturally
toggle the advanced settings
Fixes #7756
---------
Co-authored-by: Devessier
---
.../modules/ui/input/components/Toggle.tsx | 57 ++++++++-----------
.../components/AdvancedSettingsToggle.tsx | 8 ++-
.../components/VisibilityHidden.tsx | 14 +++++
.../components/VisibilityHiddenInput.tsx | 7 +++
packages/twenty-ui/src/accessibility/index.ts | 3 +
.../accessibility/utils/visibility-hidden.ts | 13 +++++
packages/twenty-ui/src/index.ts | 1 +
7 files changed, 68 insertions(+), 35 deletions(-)
create mode 100644 packages/twenty-ui/src/accessibility/components/VisibilityHidden.tsx
create mode 100644 packages/twenty-ui/src/accessibility/components/VisibilityHiddenInput.tsx
create mode 100644 packages/twenty-ui/src/accessibility/index.ts
create mode 100644 packages/twenty-ui/src/accessibility/utils/visibility-hidden.ts
diff --git a/packages/twenty-front/src/modules/ui/input/components/Toggle.tsx b/packages/twenty-front/src/modules/ui/input/components/Toggle.tsx
index 39bafac99582..cd45fdca1fba 100644
--- a/packages/twenty-front/src/modules/ui/input/components/Toggle.tsx
+++ b/packages/twenty-front/src/modules/ui/input/components/Toggle.tsx
@@ -1,8 +1,6 @@
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
-import { useEffect, useState } from 'react';
-
-import { isDefined } from '~/utils/isDefined';
+import { VisibilityHiddenInput } from 'twenty-ui';
export type ToggleSize = 'small' | 'medium';
@@ -10,10 +8,10 @@ type ContainerProps = {
isOn: boolean;
color?: string;
toggleSize: ToggleSize;
- disabled?: boolean;
+ 'data-disabled'?: boolean;
};
-const StyledContainer = styled.div`
+const StyledContainer = styled.label`
align-items: center;
background-color: ${({ theme, isOn, color }) =>
isOn ? (color ?? theme.color.blue) : theme.background.transparent.medium};
@@ -23,13 +21,15 @@ const StyledContainer = styled.div`
height: ${({ toggleSize }) => (toggleSize === 'small' ? 16 : 20)}px;
transition: background-color 0.3s ease;
width: ${({ toggleSize }) => (toggleSize === 'small' ? 24 : 32)}px;
- opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
- pointer-events: ${({ disabled }) => (disabled ? 'none' : 'auto')};
+ opacity: ${({ 'data-disabled': disabled }) => (disabled ? 0.5 : 1)};
+ pointer-events: ${({ 'data-disabled': disabled }) =>
+ disabled ? 'none' : 'auto'};
`;
-const StyledCircle = styled(motion.div)<{
+const StyledCircle = styled(motion.span)<{
size: ToggleSize;
}>`
+ display: block;
background-color: ${({ theme }) => theme.background.primary};
border-radius: 50%;
height: ${({ size }) => (size === 'small' ? 12 : 16)}px;
@@ -37,6 +37,7 @@ const StyledCircle = styled(motion.div)<{
`;
export type ToggleProps = {
+ id?: string;
value?: boolean;
onChange?: (value: boolean) => void;
color?: string;
@@ -46,49 +47,39 @@ export type ToggleProps = {
};
export const Toggle = ({
- value,
+ id,
+ value = false,
onChange,
color,
toggleSize = 'medium',
className,
disabled,
}: ToggleProps) => {
- const [isOn, setIsOn] = useState(value ?? false);
-
const circleVariants = {
on: { x: toggleSize === 'small' ? 10 : 14 },
off: { x: 2 },
};
- const handleChange = () => {
- setIsOn(!isOn);
-
- if (isDefined(onChange)) {
- onChange(!isOn);
- }
- };
-
- useEffect(() => {
- setIsOn((isOn) => {
- if (value !== isOn) {
- return value ?? false;
- }
-
- return isOn;
- });
- }, [value, setIsOn]);
-
return (
+ {
+ onChange?.(event.target.checked);
+ }}
+ />
+
diff --git a/packages/twenty-front/src/modules/ui/navigation/link/components/AdvancedSettingsToggle.tsx b/packages/twenty-front/src/modules/ui/navigation/link/components/AdvancedSettingsToggle.tsx
index e08f60031bf1..9a74cca8148c 100644
--- a/packages/twenty-front/src/modules/ui/navigation/link/components/AdvancedSettingsToggle.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/link/components/AdvancedSettingsToggle.tsx
@@ -1,6 +1,7 @@
import { Toggle } from '@/ui/input/components/Toggle';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import styled from '@emotion/styled';
+import { useId } from 'react';
import { useRecoilState } from 'recoil';
import { IconTool, MAIN_COLORS } from 'twenty-ui';
@@ -12,7 +13,7 @@ const StyledContainer = styled.div`
position: relative;
`;
-const StyledText = styled.span`
+const StyledLabel = styled.label`
color: ${({ theme }) => theme.font.color.secondary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
@@ -41,6 +42,7 @@ export const AdvancedSettingsToggle = () => {
const [isAdvancedModeEnabled, setIsAdvancedModeEnabled] = useRecoilState(
isAdvancedModeEnabledState,
);
+ const inputId = useId();
const onChange = (newValue: boolean) => {
setIsAdvancedModeEnabled(newValue);
@@ -52,8 +54,10 @@ export const AdvancedSettingsToggle = () => {
- Advanced:
+ Advanced:
+
{
+ return {children} ;
+};
diff --git a/packages/twenty-ui/src/accessibility/components/VisibilityHiddenInput.tsx b/packages/twenty-ui/src/accessibility/components/VisibilityHiddenInput.tsx
new file mode 100644
index 000000000000..01258484db19
--- /dev/null
+++ b/packages/twenty-ui/src/accessibility/components/VisibilityHiddenInput.tsx
@@ -0,0 +1,7 @@
+import styled from '@emotion/styled';
+import { VISIBILITY_HIDDEN } from '@ui/accessibility/utils/visibility-hidden';
+
+// eslint-disable-next-line @nx/workspace-styled-components-prefixed-with-styled
+export const VisibilityHiddenInput = styled.input`
+ ${VISIBILITY_HIDDEN}
+`;
diff --git a/packages/twenty-ui/src/accessibility/index.ts b/packages/twenty-ui/src/accessibility/index.ts
new file mode 100644
index 000000000000..70d4702dd067
--- /dev/null
+++ b/packages/twenty-ui/src/accessibility/index.ts
@@ -0,0 +1,3 @@
+export * from './components/VisibilityHidden';
+export * from './components/VisibilityHiddenInput';
+export * from './utils/visibility-hidden';
diff --git a/packages/twenty-ui/src/accessibility/utils/visibility-hidden.ts b/packages/twenty-ui/src/accessibility/utils/visibility-hidden.ts
new file mode 100644
index 000000000000..c00ebfe9060b
--- /dev/null
+++ b/packages/twenty-ui/src/accessibility/utils/visibility-hidden.ts
@@ -0,0 +1,13 @@
+import { css } from '@emotion/react';
+
+export const VISIBILITY_HIDDEN = css`
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+`;
diff --git a/packages/twenty-ui/src/index.ts b/packages/twenty-ui/src/index.ts
index bc26e1a7c694..7518172f6bb3 100644
--- a/packages/twenty-ui/src/index.ts
+++ b/packages/twenty-ui/src/index.ts
@@ -1,3 +1,4 @@
+export * from './accessibility';
export * from './components';
export * from './display';
export * from './layout';