Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert manage theme followup #2889

Merged
merged 14 commits into from
Jun 26, 2024
Merged
Changes from 11 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
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import {
Box, Button, IconButton, Stack,
} from '@tokens-studio/ui';
import { NavArrowLeft } from 'iconoir-react';
import { useTranslation } from 'react-i18next';
import { allTokenSetsSelector, themesListSelector, usedTokenSetSelector } from '@/selectors';
import { StyledNameInputBox } from './StyledNameInputBox';
import { StyledCreateOrEditThemeFormHeaderFlex } from './StyledCreateOrEditThemeFormHeaderFlex';
@@ -53,20 +54,29 @@ export const CreateOrEditThemeForm: React.FC<React.PropsWithChildren<React.Props
const availableTokenSets = useSelector(allTokenSetsSelector);
const themes = useSelector(themesListSelector);
const groupNames = useMemo(() => ([...new Set(themes.filter((t) => t?.group).map((t) => t.group as string))]), [themes]);
const { t } = useTranslation(['tokens', 'errors']);

const treeOrListItems = useMemo(() => (
githubMfsEnabled
? tokenSetListToTree(availableTokenSets)
: tokenSetListToList(availableTokenSets)
), [githubMfsEnabled, availableTokenSets]);

const { register, handleSubmit, control } = useForm<FormValues>({
const { register, handleSubmit, control, resetField } = useForm<FormValues>({
defaultValues: {
tokenSets: { ...selectedTokenSets },
...defaultValues,
},
});

const handleKeyDown = useCallback((fieldName: keyof FormValues) => (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Escape') {
e.stopPropagation();
resetField(fieldName);
setShowGroupInput(false);
}
}, [handleSubmit]);

const TokenSetThemeItemInput = useCallback((props: React.PropsWithChildren<{ item: TreeItem }>) => (
<Controller
name="tokenSets"
@@ -92,7 +102,7 @@ export const CreateOrEditThemeForm: React.FC<React.PropsWithChildren<React.Props
<StyledNameInputBox css={{ width: '100%' }}>
<StyledCreateOrEditThemeFormHeaderFlex>
<IconButton
tooltip="Return to overview"
tooltip={t('returnToOverview')}
data-testid="button-return-to-overview"
icon={<NavArrowLeft />}
size="small"
@@ -117,7 +127,8 @@ export const CreateOrEditThemeForm: React.FC<React.PropsWithChildren<React.Props
autofocus
data-testid="create-or-edit-theme-form--group--name"
{...register('group')}
placeholder="Add group"
placeholder={t('addGroup')}
onKeyDown={handleKeyDown('group')}
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this not be calling it all the time on any key down event? 🧐

Suggested change
onKeyDown={handleKeyDown('group')}
onKeyDown={() => handleKeyDown('group')}

would that work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed the function handleKeyDown to handleGroupKeyDown, the 'group' argument here is redundant since the function is only being used in one place.

css={{
display: 'flex',
}}
@@ -146,9 +157,8 @@ export const CreateOrEditThemeForm: React.FC<React.PropsWithChildren<React.Props
icon={<IconPlus />}
onClick={handleAddGroup}
size="small"
css={{ display: 'flex', alignItems: 'center', height: '28px' }}
>
Add&nbsp;group
{t('addGroup')}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The &nbsp; seems to be unnecessary, and feels like an antipattern to use HTML characters in locale files. Tried resizing the plugin to minimum width and the button stayed on one line.

Copy link
Collaborator

Choose a reason for hiding this comment

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

agree should also be solveable with css

</Button>
)
}
@@ -162,7 +172,7 @@ export const CreateOrEditThemeForm: React.FC<React.PropsWithChildren<React.Props
full
data-testid="create-or-edit-theme-form--input--name"
{...register('name', { required: true })}
placeholder="Theme name"
placeholder={t('themeName')}
/>
</Stack>
</Stack>
@@ -172,8 +182,8 @@ export const CreateOrEditThemeForm: React.FC<React.PropsWithChildren<React.Props
{id && (

<StyledCreateOrEditThemeFormTabsFlex>
<TabButton name={ThemeFormTabs.SETS} activeTab={activeTab} label="Sets" onSwitch={setActiveTab} small />
<TabButton name={ThemeFormTabs.STYLES_VARIABLES} activeTab={activeTab} label="Styles & Variables" onSwitch={setActiveTab} small />
<TabButton name={ThemeFormTabs.SETS} activeTab={activeTab} label={t('sets.title')} onSwitch={setActiveTab} small />
<TabButton name={ThemeFormTabs.STYLES_VARIABLES} activeTab={activeTab} label={t('stylesAndVariables')} onSwitch={setActiveTab} small />
</StyledCreateOrEditThemeFormTabsFlex>
)}
<Stack direction="column" gap={1}>
@@ -188,7 +198,9 @@ export const CreateOrEditThemeForm: React.FC<React.PropsWithChildren<React.Props
)}
{(activeTab === ThemeFormTabs.STYLES_VARIABLES && id) && (
<Box css={{ padding: '$3' }}>
<Box css={{ padding: '$1', marginBottom: '$2' }}>Note: When using multi-dimensional themes where values depend on tokens of another theme, connecting styles might not work as expected.</Box>
<Box css={{ padding: '$1', marginBottom: '$2' }}>
{t('stylesVarMultiDimensionalThemesWarning')}
</Box>
<ThemeStyleManagementForm id={id} />
</Box>
)}
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ import { TreeItem, themeListToTree } from '@/utils/themeListToTree';
import { ItemData } from '@/context';
import { checkReorder } from '@/utils/motion';
import { ensureFolderIsTogether, findOrderableTargetIndexesInThemeList } from '@/utils/dragDropOrder';
import { useTranslation } from 'react-i18next';

type Props = unknown;

@@ -46,6 +47,7 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith
const [themeEditorOpen, setThemeEditorOpen] = useState<boolean | string>(false);
const [IsThemeGroupNameEditing, setIsThemeGroupNameEditing] = useState(false);
const treeItems = themeListToTree(themes);
const { t } = useTranslation(['tokens']);

const themeEditorDefaultValues = useMemo(() => {
const themeObject = themes.find(({ id }) => id === themeEditorOpen);
@@ -60,10 +62,8 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith
}, [themes, themeEditorOpen]);

const handleClose = useCallback(() => {
if (!IsThemeGroupNameEditing) {
dispatch.uiState.setManageThemesModalOpen(false);
}
}, [IsThemeGroupNameEditing, dispatch]);
dispatch.uiState.setManageThemesModalOpen(false);
}, [dispatch]);

const handleToggleThemeEditor = useCallback((theme?: ThemeObject) => {
if (theme && typeof theme !== 'boolean') {
@@ -80,7 +80,7 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith

const handleDeleteTheme = useCallback(async () => {
if (typeof themeEditorOpen === 'string') {
const confirmDelete = await confirm({ text: 'Are you sure you want to delete this theme?', confirmAction: 'Delete', variant: 'danger' });
const confirmDelete = await confirm({ text: t('confirmDeleteTheme'), confirmAction: t('delete'), variant: 'danger' });
if (confirmDelete) {
track('Delete theme', { id: themeEditorOpen });
dispatch.tokenState.deleteTheme(themeEditorOpen);
@@ -160,7 +160,7 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith
<Modal
isOpen
full
title="Themes"
title={t('themes')}
stickyFooter
showClose
footer={(
@@ -172,7 +172,7 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith
icon={<IconPlus />}
onClick={handleToggleOpenThemeEditor}
>
New theme
{t('newTheme')}
</Button>
)}
{themeEditorOpen && (
@@ -185,7 +185,7 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith
type="submit"
onClick={handleDeleteTheme}
>
Delete
{t('delete')}
</Button>
)}
</Box>
@@ -195,15 +195,15 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith
variant="secondary"
onClick={handleToggleOpenThemeEditor}
>
Cancel
{t('cancel')}
</Button>
<Button
data-testid="button-manage-themes-modal-save-theme"
variant="primary"
type="submit"
form="form-create-or-edit-theme"
>
Save theme
{t('saveTheme')}
</Button>
</Stack>
</>
@@ -215,8 +215,8 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith
{!themes.length && !themeEditorOpen && (
<EmptyState
css={{ padding: '$8 $4' }}
title="You don't have any themes yet"
description="Create your first theme now"
title={t('manageThemesModal.emptyTitle')}
description={t('manageThemesModal.emptyDescription')}
/>
)}
{!!themes.length && !themeEditorOpen && (
@@ -237,7 +237,6 @@ export const ManageThemesModal: React.FC<React.PropsWithChildren<React.PropsWith
<ThemeListGroupHeader
label={item.value === INTERNAL_THEMES_NO_GROUP ? INTERNAL_THEMES_NO_GROUP_LABEL : item.value as string}
groupName={item.value as string}
setIsGroupEditing={handleUpdateIsEditing}
/>
)
}
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ export const ThemeGroupDropDownMenu: React.FC<React.PropsWithChildren<React.Prop
<DropdownMenu.Trigger asChild>
{
selectedGroup ? (
<Button variant="secondary" asDropdown>
<Button variant="secondary" asDropdown size="small">
<span>{selectedGroup}</span>
</Button>
) : (
@@ -54,10 +54,9 @@ export const ThemeGroupDropDownMenu: React.FC<React.PropsWithChildren<React.Prop
variant="secondary"
icon={<IconPlus />}
size="small"
css={{ display: 'flex', alignItems: 'center', height: '28px' }}
asDropdown
>
Add&nbsp;group
{t('addGroup')}
</Button>
</Box>
)
Original file line number Diff line number Diff line change
@@ -13,42 +13,47 @@ import Input from '../Input';
import IconPencil from '@/icons/pencil.svg';
import { Dispatch } from '@/app/store';
import { INTERNAL_THEMES_NO_GROUP } from '@/constants/InternalTokenGroup';
import { Xmark, Check } from 'iconoir-react';

type Props = React.PropsWithChildren<{
groupName: string
label: string
setIsGroupEditing: (value: boolean) => void
}>;

export function ThemeListGroupHeader({
groupName,
label,
setIsGroupEditing,
}: Props) {
const dispatch = useDispatch<Dispatch>();
const dragContext = useContext(DragControlsContext);
const editProhibited = useSelector(editProhibitedSelector);
const [currentGroupName, setCurrentGroupName] = useState<string>(groupName === INTERNAL_THEMES_NO_GROUP ? '' : groupName);
const [isEditing, setIsEditing] = useState(false);
const handleDragStart = useCallback((event: React.PointerEvent<HTMLDivElement>) => {
dragContext.controls?.start(event);
}, [dragContext.controls]);
const [isGroupNameEditing, setIsGroupNameEditing] = useState<boolean>(false);

const handleEditButtonClick = useCallback(() => {
setIsEditing(true);
setIsGroupEditing(true);
}, [setIsEditing, setIsGroupEditing]);
setIsGroupNameEditing(true);
}, [setIsGroupNameEditing]);
const handleCancel = useCallback(() => {
setCurrentGroupName(groupName);
setIsGroupNameEditing(false);
}, [setIsGroupNameEditing]);

const handleSubmit = React.useCallback(() => {
dispatch.tokenState.updateThemeGroupName(groupName, currentGroupName);
setIsGroupNameEditing(false);
}, [currentGroupName, groupName]);

const handleKeyDown = React.useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
dispatch.tokenState.updateThemeGroupName(groupName, currentGroupName);
setIsEditing(false);
setIsGroupEditing(false);
handleSubmit();
} else if (e.key === 'Escape') {
setIsEditing(false);
setIsGroupEditing(false);
e.stopPropagation();
handleCancel();
}
}, [currentGroupName, groupName, dispatch.tokenState, setIsEditing, setIsGroupEditing]);
}, [currentGroupName, groupName, dispatch.tokenState]);

const handleGroupNameChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setCurrentGroupName(event.target.value);
@@ -59,6 +64,7 @@ export function ThemeListGroupHeader({
type="button"
canReorder={!editProhibited}
css={{
marginBottom: '$2',
backgroundColor: '$bgDefault',
display: 'flex',
cursor: 'inherit',
@@ -73,6 +79,7 @@ export function ThemeListGroupHeader({
<Box css={{
display: 'flex',
alignItems: 'center',
gap: '$2',
'& > div > button ': {
display: 'none',
},
@@ -81,7 +88,7 @@ export function ThemeListGroupHeader({
},
}}
>
{!isEditing ? (
{!isGroupNameEditing ? (
<>
<Text css={{
color: '$fgMuted', height: '$controlSmall', display: 'flex', alignItems: 'center',
@@ -98,15 +105,31 @@ export function ThemeListGroupHeader({
/>
</>
) : (
<Input
type="text"
name={`groupName-${groupName}`}
value={currentGroupName}
onChange={handleGroupNameChange}
onKeyDown={handleKeyDown}
autofocus
full
/>
<>
<Input
type="text"
name={`groupName-${groupName}`}
value={currentGroupName}
onChange={handleGroupNameChange}
onKeyDown={handleKeyDown}
autofocus
full
/>
<IconButton
onClick={handleCancel}
icon={<Xmark />}
color="$dangerBorder"
size="small"
tooltip="Cancel"
/>
<IconButton
onClick={handleSubmit}
icon={<Check />}
size="small"
variant="primary"
tooltip="Confirm"
/>
</>
)}
</Box>
</StyledDragButton>
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ import Stack from '../Stack';
import { Count } from '../Count';
import Checkbox from '../Checkbox';
import Label from '../Label';
import { useTranslation } from 'react-i18next';

type Props = {
label: string
@@ -33,6 +34,7 @@ export const ThemeStyleManagementCategory: React.FC<React.PropsWithChildren<Reac
isWaitingForBackgroundJobSelector(state, BackgroundJobs.UI_ATTACHING_LOCAL_STYLES)
), []));
const [selectedStyles, setSelectedStyles] = useState<string[]>([]);
const { t } = useTranslation(['tokens', 'errors']);

const stylesEntries = useMemo(() => Object.entries(styles), [styles]);

@@ -74,7 +76,7 @@ export const ThemeStyleManagementCategory: React.FC<React.PropsWithChildren<Reac
disabled={isAttachingLocalStyles}
onClick={onAttachLocalStyles}
>
Attach local styles
{t('attachLocalStyles')}
</Button>
)}
isOpenByDefault={false}
@@ -100,12 +102,12 @@ export const ThemeStyleManagementCategory: React.FC<React.PropsWithChildren<Reac
onCheckedChange={handleSelectAll}
/>
<Label htmlFor="detachSelected" css={{ fontSize: '$small', fontWeight: '$sansBold' }}>
Select all
{t('selectAll')}
</Label>
</Stack>
<Stack gap={1}>
<Button onClick={handleDisconnectSelectedStyles} disabled={selectedStyles.length === 0} variant="danger" size="small">
Detach selected
{t('detachSelected')}
</Button>
</Stack>
</Stack>
Loading