Skip to content

Commit

Permalink
feat(core): use figma variables to store/export raddii slices
Browse files Browse the repository at this point in the history
introduced logic to init the morfeo variable collection and save on the state the radii variables
found. Add also some defaults if there are no valid radii variables found

wip #27
  • Loading branch information
gabrieleAngius committed Jun 30, 2023
1 parent 243d05f commit 40175c8
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 23 deletions.
36 changes: 36 additions & 0 deletions test-environment/getFigmaMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ const getNodeWithDefaults = (jestFn, type) => ({
remove: jestFn(() => {}),
});

const getVariableBase = (jestFn) => ({
id: randomId(),
description: "",
key: "",
remote: false,
scopes: [],
valuesByMode: {},
remove: jestFn(),
resolveForConsumer: jestFn(),
setValueForMode: jestFn(),
});

/** @type {(() => void) => {}} */
module.exports.getFigmaMock = (jestFn) => {
return {
Expand All @@ -24,6 +36,7 @@ module.exports.getFigmaMock = (jestFn) => {
}),
currentPage: {
selection: [],
children: [],
},
viewport: {
scrollAndZoomIntoView: jestFn(),
Expand Down Expand Up @@ -56,6 +69,29 @@ module.exports.getFigmaMock = (jestFn) => {
widget: {
useEffect: jestFn((callback) => callback()),
},
variables: {
createVariable: jestFn((name, collectionId, resolvedType) => ({
name,
collectionId,
resolvedType,
...getVariableBase(jestFn),
})),
createVariableAlias: jestFn(() => ({
id: randomId(),
type: "VARIABLE_ALIAS",
})),
createVariableCollection: jestFn((name) => ({
id: randomId(),
defaultModeId: randomId(),
name,
})),
getLocalVariableCollections: jestFn(() => []),
getLocalVariables: jestFn(() => []),
getVariableById: jestFn(() => null),
getVariableCollectionById: jestFn(() => null),
importVariableByKeyAsync: jestFn(),
setBoundVariableForPaint: jestFn(),
},
getLocalPaintStyles: jestFn(() => []),
getLocalTextStyles: jestFn(() => []),
getLocalEffectStyles: jestFn(() => []),
Expand Down
5 changes: 3 additions & 2 deletions widget-src/components/Radii/RadiiSlice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface Props extends RadiiSliceItem {
}

export const RadiiSlice = ({ store, ...slice }: Props) => {
const inputName = slice.name.replace("Radius/", "");
return (
<AutoLayout
name="Radius Slice"
Expand Down Expand Up @@ -37,8 +38,8 @@ export const RadiiSlice = ({ store, ...slice }: Props) => {
fill="#000"
fontFamily="Inter"
fontWeight={700}
value={slice.name}
width={40}
value={inputName}
width={100}
inputBehavior="truncate"
inputFrameProps={{ direction: "horizontal" }}
onTextEditEnd={() => {}}
Expand Down
10 changes: 7 additions & 3 deletions widget-src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const defaultColorSliceItems: ColorSliceItem[] = [
},
];

export enum ComponentNames {
Box = "BOX",
}
export const defaultRadiiSliceItems = [
{ name: "Radius/none", value: 0 },
{ name: "Radius/XS", value: 5 },
{ name: "Radius/S", value: 10 },
];

export const MORFEO_COLLECTION_NAME = "Morfeo tokens";
71 changes: 71 additions & 0 deletions widget-src/hooks/useInitCollection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { MORFEO_COLLECTION_NAME } from "../constants";
import { useInitCollection } from "./useInitCollection";

describe("useInitCollection", () => {
it("should do nothing if the collection already exist", () => {
jest
.spyOn(figma.variables, "getVariableCollectionById")
.mockReturnValue({} as any);
const mockSetCollection = jest.fn();

useInitCollection(
{ id: "", defaultModeId: "", name: "" },
mockSetCollection
);

expect(mockSetCollection).not.toBeCalled();
expect(figma.variables.createVariableCollection).not.toBeCalled();
});

it("should set the state if getVariableCollectionById doesn't find anything and there's a collection with matching name", () => {
jest
.spyOn(figma.variables, "getVariableCollectionById")
.mockReturnValue(null);

jest.spyOn(figma.variables, "getLocalVariableCollections").mockReturnValue([
{
id: "morfeo:collection:id",
defaultModeId: "default:mode:id",
name: MORFEO_COLLECTION_NAME,
} as any,
]);
const mockSetCollection = jest.fn();

useInitCollection(
{ id: "", defaultModeId: "", name: "" },
mockSetCollection
);

expect(mockSetCollection).toBeCalledWith({
id: "morfeo:collection:id",
defaultModeId: "default:mode:id",
name: MORFEO_COLLECTION_NAME,
});
expect(figma.variables.createVariableCollection).not.toBeCalled();
});

it("should set the collection if getVariableCollectionById doesn't find anything and there's NOT any collection with matching name", () => {
jest
.spyOn(figma.variables, "getVariableCollectionById")
.mockReturnValue(null);

jest
.spyOn(figma.variables, "getLocalVariableCollections")
.mockReturnValue([]);
const mockSetCollection = jest.fn();

useInitCollection(
{ id: "", defaultModeId: "", name: "" },
mockSetCollection
);

expect(mockSetCollection).toBeCalledWith({
id: expect.any(String),
defaultModeId: expect.any(String),
name: MORFEO_COLLECTION_NAME,
});
expect(figma.variables.createVariableCollection).toBeCalledWith(
MORFEO_COLLECTION_NAME
);
});
});
31 changes: 31 additions & 0 deletions widget-src/hooks/useInitCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { MORFEO_COLLECTION_NAME } from "../constants";
import { MorfeoCollection } from "../types";

const { widget } = figma;
const { useEffect } = widget;

export const useInitCollection = (
collection: MorfeoCollection,
setCollection: (newCollection: MorfeoCollection) => void
) => {
useEffect(() => {
if (figma.variables.getVariableCollectionById(collection.id)) {
return;
}

const documentCollections = figma.variables.getLocalVariableCollections();

const morfeoCollection = documentCollections.find(
(collection) => collection.name === MORFEO_COLLECTION_NAME
);

if (morfeoCollection) {
const { id, name, defaultModeId } = morfeoCollection;
setCollection({ id, name, defaultModeId });
} else {
const { id, name, defaultModeId } =
figma.variables.createVariableCollection(MORFEO_COLLECTION_NAME);
setCollection({ id, name, defaultModeId });
}
});
};
80 changes: 67 additions & 13 deletions widget-src/hooks/useInitTheme.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,125 @@ import { mockSyncedMap } from "../test-utils/mockSyncedMap";
import { RadiiSliceItem, ColorSliceItem, Slice } from "../types";
import { useInitTheme } from "./useInitTheme";

describe.skip("useInitTheme", () => {
it("should init radii with defaults if the state is empty and there are no radius variables", () => {
describe("useInitTheme", () => {
it("should init radii with defaults and call createVariable if the state is empty and there are no radius variables", () => {
jest
.spyOn(figma.variables, "getVariableCollectionById")
.mockReturnValue({} as any);
const mockState = mockSyncedMap<RadiiSliceItem>();
useInitTheme({
[Slice.Radii]: mockState,
[Slice.Colors]: mockSyncedMap(),
morfeoCollection: { id: "collection:id", defaultModeId: "", name: "" },
});

// set the state
expect(mockState.set).toBeCalledWith(expect.any(String), {
id: expect.any(String),
name: "none",
name: "Radius/none",
value: 0,
refIds: expect.any(Array),
});
expect(mockState.set).toBeCalledWith(expect.any(String), {
id: expect.any(String),
name: "XS",
value: 1,
refIds: expect.any(Array),
name: "Radius/XS",
value: 5,
});
expect(mockState.set).toBeCalledWith(expect.any(String), {
id: expect.any(String),
name: "L",
name: "Radius/S",
value: 10,
refIds: expect.any(Array),
});

// create variables
expect(figma.variables.createVariable).toBeCalledWith(
"Radius/none",
"collection:id",
"FLOAT"
);
expect(figma.variables.createVariable).toBeCalledWith(
"Radius/XS",
"collection:id",
"FLOAT"
);
expect(figma.variables.createVariable).toBeCalledWith(
"Radius/S",
"collection:id",
"FLOAT"
);
});

it("should not set radii if the state is not empty and there are matching variables", () => {
it("should not set radii if the state is not empty", () => {
jest
.spyOn(figma.variables, "getVariableCollectionById")
.mockReturnValue({} as any);
const mockState = mockSyncedMap({
anyId: { id: "anyId", name: "A", libStyleId: "", value: 0 },
});
useInitTheme({
[Slice.Radii]: mockState,
[Slice.Colors]: mockSyncedMap(),
morfeoCollection: { id: "", defaultModeId: "", name: "" },
});
expect(mockState.set).not.toBeCalled();
});

it("should not set radii if the morfeo collection is not found", () => {
jest
.spyOn(figma.variables, "getVariableCollectionById")
.mockReturnValue(null);
const mockState = mockSyncedMap<RadiiSliceItem>();
useInitTheme({
[Slice.Radii]: mockState,
[Slice.Colors]: mockSyncedMap(),
morfeoCollection: { id: "", defaultModeId: "", name: "" },
});
expect(mockState.set).not.toBeCalled();
});

it("should use the variables to init the radii if the state is empty and radii variables are found", () => {
jest
.spyOn(figma.variables, "getVariableCollectionById")
.mockReturnValue({ variableIds: ["id:1", "id:2"] } as any);
jest.spyOn(figma.variables, "getVariableById").mockReturnValueOnce({
id: "",
name: "A",
resolveForConsumer: () => ({ value: 10, resolvedType: "FLOAT" }),
scopes: ["CORNER_RADIUS"],
} as any);
jest.spyOn(figma.variables, "getVariableById").mockReturnValue({
id: "",
name: "B",
resolveForConsumer: () => ({ value: 20, resolvedType: "FLOAT" }),
scopes: ["CORNER_RADIUS"],
} as any);

const mockState = mockSyncedMap<RadiiSliceItem>();
useInitTheme({
[Slice.Radii]: mockState,
[Slice.Colors]: mockSyncedMap(),
morfeoCollection: { id: "", defaultModeId: "", name: "" },
});

expect(figma.combineAsVariants).not.toBeCalled();
expect(mockState.set).toBeCalledWith(expect.any(String), {
id: expect.any(String),
name: "A",
value: 10,
refIds: ["1"],
});

expect(mockState.set).toBeCalledWith(expect.any(String), {
id: expect.any(String),
name: "B",
value: 20,
refIds: ["2"],
});
});

it("should init the colors with default values and add them to the library if the library have not solid colours", () => {
jest.spyOn(figma, "getLocalPaintStyles").mockReturnValue([]);
const mockColorsState = mockSyncedMap<ColorSliceItem>();
useInitTheme({
[Slice.Radii]: mockSyncedMap(),
[Slice.Colors]: mockColorsState,
morfeoCollection: { id: "", defaultModeId: "", name: "" },
});
expect(mockColorsState.set).toBeCalledWith(defaultColorSliceItems[0].id, {
...defaultColorSliceItems[0],
Expand All @@ -95,6 +148,7 @@ describe.skip("useInitTheme", () => {
useInitTheme({
[Slice.Radii]: mockSyncedMap(),
[Slice.Colors]: mockColorsState,
morfeoCollection: { id: "", defaultModeId: "", name: "" },
});
expect(mockColorsState.set).toBeCalledWith("paint-1", {
id: expect.any(String),
Expand Down
Loading

0 comments on commit 40175c8

Please sign in to comment.