Skip to content

Commit

Permalink
[Export Styles] Add download button to variable export (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
origami-z authored Jan 10, 2024
1 parent e3fe36e commit 0db2ab1
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/export-styles/ui-src/App.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#root,
.appRoot,
.saltFlexLayout {
.viewRoot {
height: 100%;
}

Expand Down
30 changes: 30 additions & 0 deletions packages/export-styles/ui-src/__tests__/VariableJsonView.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import userEvents from "@testing-library/user-event";
import React from "react";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { VariableJsonView } from "../views/VariableJsonView";
import * as utilExports from "../components/utils";

// Mock out download, `URL.createObjectURL` not supported in test env
vi.spyOn(utilExports, "downloadBlob").mockImplementation(() => {});

describe("VariableJsonView", () => {
beforeEach(() => {
Expand Down Expand Up @@ -161,4 +165,30 @@ describe("VariableJsonView", () => {
).toEqual("");
expect(screen.getByRole("button", { name: "Export" })).toBeDisabled();
});
test("download JSON button is disabled until export text is available", async () => {
const downloadJsonButton = screen.getByRole("button", {
name: "Download JSON",
});
expect(downloadJsonButton).toHaveAttribute("aria-disabled", "true");

const fileName = "filename.json";
const expectedText = "text to be filled in";
fireEvent(
window,
new MessageEvent("message", {
data: {
pluginMessage: {
type: "export-variable-to-json-result",
body: expectedText,
fileName: fileName,
},
},
})
);
expect(downloadJsonButton).not.toHaveAttribute("aria-disabled", "true");

await userEvents.click(downloadJsonButton);

expect(utilExports.downloadBlob).toHaveBeenCalledOnce();
});
});
29 changes: 29 additions & 0 deletions packages/export-styles/ui-src/components/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const downloadDataUri = (dataUri: string, fileName: string) => {
const link = document.createElement("a");
link.href = dataUri;
link.download = fileName;
// some browser needs the anchor to be in the doc
document.body.append(link);
link.click();
link.remove();
// in case the Blob uses a log of memory
setTimeout(() => {
URL.revokeObjectURL(link.href);
}, 7000);
};

/**
* This is much more efficient than manually converting a Blob or Uint8Array (e.g. `new Blob([uint8Array])`) to string / dataUri.
*/
export const downloadBlob = (blob: Blob, fileName: string) => {
const link = document.createElement("a");
// create a blobURI pointing to our Blob
link.href = URL.createObjectURL(blob);
link.download = fileName;
// some browser needs the anchor to be in the doc
document.body.append(link);
link.click();
link.remove();
// in case the Blob uses a lot of memory
setTimeout(() => URL.revokeObjectURL(link.href), 7000);
};
2 changes: 1 addition & 1 deletion packages/export-styles/ui-src/views/ExportCssView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const ExportCssView = () => {
}, []);

return (
<StackLayout gap={1}>
<StackLayout gap={1} className="viewRoot">
<FormField label="Prefix" labelPlacement="left">
<Input
value={prefix}
Expand Down
2 changes: 1 addition & 1 deletion packages/export-styles/ui-src/views/ExportJsonView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const ExportJsonView = () => {
}, []);

return (
<StackLayout gap={1}>
<StackLayout gap={1} className="viewRoot">
<FormField label="Format" labelPlacement="left">
<Dropdown
source={ExportColorAllFormats}
Expand Down
33 changes: 26 additions & 7 deletions packages/export-styles/ui-src/views/VariableJsonView.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Button, StackLayout, Text } from "@salt-ds/core";
import { Button, FlexLayout, StackLayout, Text, Tooltip } from "@salt-ds/core";
import { DownloadIcon } from "@salt-ds/icons";
import { Dropdown, FormField } from "@salt-ds/lab";
import React, { useEffect, useId, useRef, useState } from "react";
import {
ExportColorAllFormats,
ExportColorFormat,
FigmaVariableCollection,
FigmaVariableMode,
PostToFigmaMessage,
} from "../../shared-src";
import { downloadBlob } from "../components/utils";
import { FigmaToUIMessageEvent } from "../types";

export const VariableJsonView = () => {
Expand Down Expand Up @@ -87,8 +87,14 @@ export const VariableJsonView = () => {
};
}, []);

const onDownload = () => {
var blob = new Blob([text], { type: "application/json" });
// Use blob instead of plain text downloadDataUri, as Safari wipes out new line
downloadBlob(blob, fileName);
};

return (
<StackLayout gap={1}>
<StackLayout gap={1} className="viewRoot">
<FormField label="Collection">
<Dropdown
source={collections}
Expand Down Expand Up @@ -137,9 +143,22 @@ export const VariableJsonView = () => {
spellCheck={false}
aria-labelledby={exportLabel}
></textarea>
<Button onClick={onCopy} ref={copyButtonRef}>
Copy
</Button>
<FlexLayout gap={1}>
<Button onClick={onCopy} ref={copyButtonRef} style={{ flex: 1 }}>
Copy
</Button>

<Tooltip placement="top" content="Download data as JSON">
<Button
focusableWhenDisabled
onClick={onDownload}
disabled={!fileName}
aria-label="Download JSON"
>
<DownloadIcon />
</Button>
</Tooltip>
</FlexLayout>
</StackLayout>
);
};

0 comments on commit 0db2ab1

Please sign in to comment.