Skip to content

Commit

Permalink
Merge pull request #950 from visualize-admin/refactor/nextkit
Browse files Browse the repository at this point in the history
refactor: Use nextkit for api routes
  • Loading branch information
ptbrowne authored Feb 1, 2023
2 parents c559eec + 97991e5 commit 06984ff
Show file tree
Hide file tree
Showing 16 changed files with 409 additions and 389 deletions.
150 changes: 150 additions & 0 deletions app/components/copy-to-clipboard-text-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { Trans } from "@lingui/macro";
import { Button, Input, Theme } from "@mui/material";
import { makeStyles } from "@mui/styles";
import * as clipboard from "clipboard-polyfill/text";
import { MouseEvent as ReactMouseEvent, ReactNode, useState } from "react";

import Flex, { FlexProps } from "@/components/flex";
import { Icon } from "@/icons";

const useActionTooltipStyles = makeStyles((theme: Theme) => ({
actionTooltip: {
position: "absolute",
bottom: "100%",
left: "50%",
transform: "translate3d(-50%, 0, 0)",

backgroundColor: theme.palette.grey[700],
borderRadius: 1.5,
color: theme.palette.grey[100],

fontSize: "0.625rem",
textAlign: "center",
whiteSpace: "nowrap",

padding: `${theme.spacing(1)} ${theme.spacing(2)}`,
marginBottom: "calc(0.5rem + 2px)",

zIndex: 13,
pointerEvents: "none",
filter: "0 3px 5px 0 rgba(0,0,0,0.90)",

"&::after": {
content: "''",
position: "absolute",
width: 0,
height: 0,
border: "0.5rem solid transparent",
borderTopColor: theme.palette.grey[700],
left: "50%",
top: "100%",
zIndex: -1,
transform: "translateX(-50%)",
},
},
}));

export const useCopyToClipboardTextInputStyles = makeStyles((theme: Theme) => ({
input: {
color: theme.palette.grey[700],
padding: `${theme.spacing(0)} ${theme.spacing(2)}`,
flexGrow: 1,
fontSize: "1rem",
minWidth: 160,
overflowX: "auto",
borderTopLeftRadius: "default",
borderBottomLeftRadius: "default",
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
borderWidth: "1px",
borderStyle: "solid",
borderColor: theme.palette.grey[500],
},
button: {
color: theme.palette.grey[600],
backgroundColor: theme.palette.grey[200],
position: "relative",

borderTopRightRadius: "default",
borderBottomRightRadius: "default",
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
width: 48,
minWidth: 48,

borderWidth: "1px",
borderStyle: "solid",
borderColor: theme.palette.grey[500],
borderLeft: "none",

cursor: "pointer",

"&:hover": {
backgroundColor: theme.palette.grey[300],
color: theme.palette.grey[700],
},
"&:active": {
backgroundColor: theme.palette.grey[400],
color: theme.palette.grey[800],
},
"&:disabled": {
cursor: "initial",
color: theme.palette.grey[300],
},
},
}));

export const ActionTooltip = ({ children }: { children: ReactNode }) => {
const classes = useActionTooltipStyles();
return <div className={classes.actionTooltip}>{children}</div>;
};

export const CopyToClipboardTextInput = ({
content,
...flexProps
}: { content: string } & FlexProps) => {
const [showTooltip, toggleTooltip] = useState(false);
const [tooltipContent, updateTooltipContent] = useState(
<Trans id="button.hint.click.to.copy">click to copy</Trans>
);

const handleMouseLeave = () => {
toggleTooltip(false);
updateTooltipContent(
<Trans id="button.hint.click.to.copy">click to copy</Trans>
);
};
const handleClick = (
e: ReactMouseEvent<HTMLButtonElement, MouseEvent>,
content: string
) => {
e.preventDefault();
clipboard.writeText(content);
};
const classes = useCopyToClipboardTextInputStyles();
return (
<Flex sx={{ alignItems: "stretch", height: 48 }} {...flexProps}>
<Input
className={classes.input}
type="text"
value={content}
readOnly={true}
></Input>

<Button
variant="text"
onMouseOver={() => toggleTooltip(true)}
onMouseUp={() =>
updateTooltipContent(<Trans id="button.hint.copied">copied!</Trans>)
}
onMouseLeave={handleMouseLeave}
onClick={(e) => handleClick(e, content)}
className={classes.button}
>
<Icon name="copy" size={16} />

{showTooltip && <ActionTooltip>{tooltipContent}</ActionTooltip>}
</Button>
</Flex>
);
};
159 changes: 7 additions & 152 deletions app/components/publish-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,16 @@ import {
Divider,
FormControl,
FormControlLabel,
Input,
Link,
Popover,
PopoverProps,
Radio,
RadioGroup,
RadioGroupProps,
Theme,
Typography,
} from "@mui/material";
import { Stack } from "@mui/material";
import { makeStyles } from "@mui/styles";
import * as clipboard from "clipboard-polyfill/text";
import {
MouseEvent as ReactMouseEvent,
ReactNode,
useEffect,
useState,
} from "react";
import { ReactNode, useEffect, useState } from "react";

import Flex from "@/components/flex";
import { IconLink } from "@/components/links";
Expand All @@ -33,6 +24,8 @@ import { useLocale } from "@/locales/use-locale";
import { useEmbedOptions } from "@/utils/embed";
import { useI18n } from "@/utils/use-i18n";

import { CopyToClipboardTextInput } from "./copy-to-clipboard-text-input";

export const PublishActions = ({
configKey,
sx,
Expand Down Expand Up @@ -329,7 +322,9 @@ export const Embed = ({ configKey, locale }: EmbedShareProps) => {
</Typography>

<CopyToClipboardTextInput
iFrameCode={`<iframe src="${embedIframeUrl}" style="border:0px #ffffff none;" name="visualize.admin.ch" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="${iFrameHeight}" width="600px" allowfullscreen></iframe>`}
mt={1}
mb={2}
content={`<iframe src="${embedIframeUrl}" style="border:0px #ffffff none;" name="visualize.admin.ch" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="${iFrameHeight}" width="600px" allowfullscreen></iframe>`}
/>
</div>

Expand All @@ -346,149 +341,9 @@ export const Embed = ({ configKey, locale }: EmbedShareProps) => {
</Trans>
</Typography>

<CopyToClipboardTextInput iFrameCode={embedAEMUrl} />
<CopyToClipboardTextInput mt={1} mb={2} content={embedAEMUrl} />
</div>
</Box>
</TriggeredPopover>
);
};

const useCopyToClipboardTextInputStyles = makeStyles((theme: Theme) => ({
input: {
color: theme.palette.grey[700],
padding: `${theme.spacing(0)} ${theme.spacing(2)}`,
flexGrow: 1,
fontSize: "1rem",
minWidth: 160,
overflowX: "auto",
borderTopLeftRadius: "default",
borderBottomLeftRadius: "default",
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
borderWidth: "1px",
borderStyle: "solid",
borderColor: theme.palette.grey[500],
},
button: {
color: theme.palette.grey[600],
backgroundColor: theme.palette.grey[200],
position: "relative",

borderTopRightRadius: "default",
borderBottomRightRadius: "default",
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
width: 48,
minWidth: 48,

borderWidth: "1px",
borderStyle: "solid",
borderColor: theme.palette.grey[500],
borderLeft: "none",

cursor: "pointer",

"&:hover": {
backgroundColor: theme.palette.grey[300],
color: theme.palette.grey[700],
},
"&:active": {
backgroundColor: theme.palette.grey[400],
color: theme.palette.grey[800],
},
"&:disabled": {
cursor: "initial",
color: theme.palette.grey[300],
},
},
}));

const CopyToClipboardTextInput = ({ iFrameCode }: { iFrameCode: string }) => {
const [showTooltip, toggleTooltip] = useState(false);
const [tooltipContent, updateTooltipContent] = useState(
<Trans id="button.hint.click.to.copy">click to copy</Trans>
);

const handleMouseLeave = () => {
toggleTooltip(false);
updateTooltipContent(
<Trans id="button.hint.click.to.copy">click to copy</Trans>
);
};
const handleClick = (
e: ReactMouseEvent<HTMLButtonElement, MouseEvent>,
iFrameCode: string
) => {
e.preventDefault();
clipboard.writeText(iFrameCode);
};
const classes = useCopyToClipboardTextInputStyles();
return (
<Flex sx={{ alignItems: "stretch", height: 48 }} mt={1} mb={2}>
<Input
className={classes.input}
type="text"
value={iFrameCode}
readOnly={true}
></Input>

<Button
variant="text"
onMouseOver={() => toggleTooltip(true)}
onMouseUp={() =>
updateTooltipContent(<Trans id="button.hint.copied">copied!</Trans>)
}
onMouseLeave={handleMouseLeave}
onClick={(e) => handleClick(e, iFrameCode)}
className={classes.button}
>
<Icon name="copy" size={16} />

{showTooltip && <ActionTooltip>{tooltipContent}</ActionTooltip>}
</Button>
</Flex>
);
};

const useStyles = makeStyles((theme: Theme) => ({
actionTooltip: {
position: "absolute",
bottom: "100%",
left: "50%",
transform: "translate3d(-50%, 0, 0)",

backgroundColor: theme.palette.grey[700],
borderRadius: 1.5,
color: theme.palette.grey[100],

fontSize: "0.625rem",
textAlign: "center",
whiteSpace: "nowrap",

padding: `${theme.spacing(1)} ${theme.spacing(2)}`,
marginBottom: "calc(0.5rem + 2px)",

zIndex: 13,
pointerEvents: "none",
filter: "0 3px 5px 0 rgba(0,0,0,0.90)",

"&::after": {
content: "''",
position: "absolute",
width: 0,
height: 0,
border: "0.5rem solid transparent",
borderTopColor: theme.palette.grey[700],
left: "50%",
top: "100%",
zIndex: -1,
transform: "translateX(-50%)",
},
},
}));

// Form
const ActionTooltip = ({ children }: { children: ReactNode }) => {
const classes = useStyles();
return <div className={classes.actionTooltip}>{children}</div>;
};
8 changes: 5 additions & 3 deletions app/configurator/configurator-state.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ import covid19TableChartConfig from "@/test/__fixtures/config/dev/chartConfig-ta
import { data as fakeVizFixture } from "@/test/__fixtures/config/prod/line-1.json";
import bathingWaterMetadata from "@/test/__fixtures/data/DataCubeMetadataWithComponentValues-bathingWater.json";
import covid19Metadata from "@/test/__fixtures/data/DataCubeMetadataWithComponentValues-covid19.json";
import * as api from "@/utils/chart-config/exchange";
import * as api from "@/utils/chart-config/api";

const mockedApi = api as jest.Mocked<typeof api>;

jest.mock("@/utils/chart-config/exchange", () => ({
jest.mock("@/utils/chart-config/api", () => ({
fetchChartConfig: jest.fn(),
}));

Expand Down Expand Up @@ -86,7 +86,9 @@ afterEach(() => {

describe("initChartStateFromChart", () => {
const setup = ({ chartConfig }: { chartConfig: object }) => {
mockedApi.fetchChartConfig.mockResolvedValue(chartConfig);
mockedApi.fetchChartConfig.mockResolvedValue(
chartConfig as ReturnType<typeof api.fetchChartConfig>
);
};
it("should fetch work if existing chart is valid", async () => {
setup({
Expand Down
7 changes: 2 additions & 5 deletions app/configurator/configurator-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ import {
getDataSourceFromLocalStorage,
useDataSourceStore,
} from "@/stores/data-source";
import {
fetchChartConfig,
saveChartConfig,
} from "@/utils/chart-config/exchange";
import { fetchChartConfig, createConfig } from "@/utils/chart-config/api";
import { migrateChartConfig } from "@/utils/chart-config/versioning";
import { createChartId } from "@/utils/create-chart-id";
import { unreachableError } from "@/utils/unreachable";
Expand Down Expand Up @@ -1583,7 +1580,7 @@ const ConfiguratorStateProviderInternal = ({
case "PUBLISHING":
(async () => {
try {
const result = await saveChartConfig(state);
const result = await createConfig(state);

/**
* EXPERIMENTAL: Post back created chart ID to opener and close window.
Expand Down
Loading

1 comment on commit 06984ff

@vercel
Copy link

@vercel vercel bot commented on 06984ff Feb 1, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

visualization-tool – ./

visualization-tool-alpha.vercel.app
visualization-tool-git-main-ixt1.vercel.app
visualization-tool-ixt1.vercel.app

Please sign in to comment.